중앙 로그 서버 설정

중앙 로그 서버를 사용하면 Google Distributed Cloud 에어 갭 데이터 센터 조직을 통해 단일 위치에서 Google Distributed Cloud(GDC) 에어 갭 어플라이언스 기기의 로그를 집계, 저장, 쿼리할 수 있습니다.

이 중앙 집중식 로깅 환경을 설정하려면 데이터 센터 조직에서 감사 및 운영 로그를 위한 전용 프로젝트와 별도의 대상 버킷을 만듭니다. GDC 어플라이언스에서 해당 버킷으로 로그를 내보낸 후 데이터 센터 조직에서 Loki 및 Grafana 데이터 소스를 구성하여 내보낸 로그를 확인합니다.

프로젝트 만들기

로그를 내보낼 GDC 데이터 센터 조직에서 다음 단계를 수행해야 합니다.

  1. KUBECONFIG를 Org Management API로 설정합니다.

    export KUBECONFIG=ORG_MANAGEMENT_API_KUBECONFIG_PATH
    
  2. 로그를 내보내는 데 필요한 권한을 얻으려면 조직 IAM 관리자에게 ClusterRole 프로젝트 생성자(ClusterRole project-creator) 역할을 부여해 달라고 요청하세요. 이러한 역할에 대한 자세한 내용은 IAM 권한 준비를 참조하세요.

  3. 프로젝트 커스텀 리소스를 적용하여 로그를 내보낼 GDC 어플라이언스의 고유한 프로젝트를 만듭니다.

    kubectl apply -f - <<EOF
    apiVersion: resourcemanager.gdc.goog/v1
    kind: Project
    metadata:
      namespace: platform
      name: APPLIANCE_PROJECT_NAME
      labels:                                                                                                                                                                                                                                                                   
        object.gdc.goog/tenant-category: user                                                                                                                                   
    EOF
    
  4. GDC 어플라이언스에서 새 프로젝트를 사용할 수 있는지 확인합니다.

    kubectl get namespace APPLIANCE_PROJECT_NAME
    
  5. 새 프로젝트를 결제 계정에 연결합니다. 프로젝트 리소스 비용을 추적하려면 프로젝트에 연결된 결제 계정이 있어야 합니다.

  6. 로그를 내보내는 데 필요한 권한을 얻으려면 조직 IAM 관리자에게 네임스페이스 APPLIANCE_PROJECT_NAME의 프로젝트 IAM 관리자(project-iam-admin) 역할을 부여해 달라고 요청하세요.

버킷 만들기

다음 단계는 로그를 내보낼 GDC 데이터 센터 조직의 플랫폼 관리자(PA)가 수행해야 합니다.

  1. KUBECONFIG를 Org Management API로 설정합니다.

    export KUBECONFIG=ORG_MANAGEMENT_API_KUBECONFIG_PATH
    
  2. 로그를 내보내는 데 필요한 권한을 얻으려면 조직 IAM 관리자에게 APPLIANCE_PROJECT_NAME 네임스페이스의 프로젝트 버킷 관리자(project-bucket-admin) 역할을 부여해 달라고 요청하세요.

  3. 버킷 커스텀 리소스를 적용하여 버킷을 만듭니다.

    apiVersion: object.gdc.goog/v1
    kind: Bucket
    metadata:
      name: BUCKET_NAME
      namespace: APPLIANCE_PROJECT_NAME
      labels:                                                                                                                                                                     
        object.gdc.goog/bucket-type: normal                                                                                                                                       
        object.gdc.goog/encryption-version: v2                                                                                                                                    
        object.gdc.goog/tenant-category: user
    spec:                                                                                                                                                                         
      description: Bucket for storing appliance xyz audit logs                                                                                                                     
      location: zone1                                                                                                                                                             
      storageClass: Standard
    
  4. 버킷이 생성되면 다음을 실행하여 버킷의 세부정보를 확인합니다.

    kubectl describe buckets BUCKET_NAME -n APPLIANCE_PROJECT_NAME
    
  5. 버킷의 객체에 액세스하기 위한 ProjectServiceAccount를 만듭니다.

    kubectl apply -f - <<EOF
    ---
    apiVersion: resourcemanager.gdc.goog/v1
    kind: ProjectServiceAccount
    metadata:
      name: BUCKET_NAME-read-write-sa
      namespace: APPLIANCE_PROJECT_NAME
    spec: {}
    EOF
    
  6. ProjectServiceAccount가 전파되었는지 확인합니다.

    kubectl get projectserviceaccount BUCKET_NAME-read-write-sa -n APPLIANCE_PROJECT_NAME -o json | jq '.status'
    
  7. ServiceAccount에 버킷에 대한 read 및 write 권한이 추가되었는지 확인합니다.

    kubectl apply -f - <<EOF
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: BUCKET_NAME-read-write-role
      namespace: APPLIANCE_PROJECT_NAME
    rules:
    - apiGroups:
      - object.gdc.goog
      resourceNames:
      - BUCKET_NAME
      resources:
      - buckets
      verbs:
      - read-object
      - write-object
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: BUCKET_NAME-read-write-rolebinding
      namespace: APPLIANCE_PROJECT_NAME
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: BUCKET_NAME-read-write-role
    subjects:
    - kind: ServiceAccount
      name: BUCKET_NAME-read-write-sa
      namespace: APPLIANCE_PROJECT_NAME
    EOF
    
  8. 버킷의 액세스 사용자 인증 정보가 포함된 보안 비밀을 가져옵니다.

    kubectl get secret -n APPLIANCE_PROJECT_NAME -o json| jq --arg jq_src BUCKET_NAME-read-write-sa '.items[].metadata|select(.annotations."object.gdc.goog/subject"==$jq_src)|.name'
    

    출력은 버킷의 보안 비밀 이름이 표시되는 다음 예시와 같이 표시되어야 합니다.

    "object-storage-key-sysstd-sa-olxv4dnwrwul4bshu37ikebgovrnvl773owaw3arx225rfi56swa"
    
  9. 값을 변수로 내보냅니다.

    export BUCKET_RW_SECRET_NAME=BUCKET_RW_SECRET_NAME
    
  10. 버킷 액세스를 위한 키 ID를 가져옵니다.

    kubectl get secret $BUCKET_RW_SECRET_NAME -n appliance-xyz -o json | jq -r '.data."access-key-id"' | base64 -di
    

    출력은 다음 예시와 같이 표시됩니다.

    PCEW2HU47Y8ACUWQO4SK
    
  11. 버킷의 보안 비밀 액세스 키를 가져옵니다.

    kubectl get secret $BUCKET_RW_SECRET_NAME -n appliance-xyz -o json | jq -r '.data."secret-access-key"' | base64 -di
    

    출력은 다음 예시와 같이 표시됩니다.

    TzGdAbgp4h2i5UeiYa9k09rNPFQ2tkYADs67+65E
    
  12. 버킷의 엔드포인트를 가져옵니다.

    kubectl get bucket BUCKET_NAME -n APPLIANCE_PROJECT_NAME -o json | jq '.status.endpoint'
    

    출력은 다음 예시와 같이 표시됩니다.

    https://objectstorage.org-1.zone1.google.gdch.test
    
  13. 버킷의 정규화된 이름을 가져옵니다.

    kubectl get bucket BUCKET_NAME -n APPLIANCE_PROJECT_NAME -o json | jq '.status.fullyQualifiedName'
    

    출력은 다음 예시와 같이 표시됩니다.

    aaaoa9a-logs-bucket
    

GDC에서 데이터 전송

원격 버킷으로 로그 내보내기에 따라 GDC 어플라이언스에서 GDC 에어 갭 적용 데이터 센터에 이전에 만든 버킷으로 로그를 전송합니다. 이때 버킷의 엔드포인트, 정규화된 이름, 액세스 키 ID, 보안 비밀 액세스 키를 사용합니다.

Google Distributed Cloud 에어 갭 데이터 센터에서 Loki 및 Grafana 설정

로그를 내보낸 GDC 에어 갭 데이터 센터 조직의 인프라 운영자(IO)가 다음 단계를 수행해야 합니다.

IAM 역할 획득

로그를 내보내는 데 필요한 권한을 얻으려면 조직 IAM 관리자에게 인프라 클러스터의 obs-system 네임스페이스에 있는 로그 복원 관리자(logs-restore-admin) 역할과 관리 영역의 obs-system 네임스페이스에 있는 데이터 소스 뷰어(datasource-viewer) 및 데이터 소스 편집자(datasource-editor) 역할을 부여해 달라고 요청하세요.

Loki 설정

  1. KUBECONFIG를 조직 인프라 클러스터로 설정합니다.

    export KUBECONFIG=ORG_INFRA_CLUSTER_KUBECONFIG_PATH
    
  2. PA에서 어플라이언스 로그 버킷의 액세스 키 ID와 보안 비밀 액세스 키를 가져오고 obs-system 네임스페이스에 사용자 인증 정보가 포함된 보안 비밀을 만듭니다.

    kubectl create secret generic -n obs-system APPLIANCE_LOGS_BUCKET_SECRET_NAME 
    --from-literal=access-key-id=APPLIANCE_LOGS_BUCKET_ACCESS_KEY_ID 
    --from-literal=secret-access-key=APPLIANCE_LOGS_BUCKET_SECRET_ACCESS_KEY
    
  3. PA에서 어플라이언스 로그 버킷의 엔드포인트와 정규화된 이름을 가져와 Loki configmap를 만듭니다.

    kubectl apply -f - <<EOF
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: CONFIGMAP_NAME
      namespace: obs-system
    data:
      loki.yaml: |-
        auth_enabled: true
        common:
          ring:
            kvstore:
              store: inmemory
        compactor:
          working_directory: /data/loki/compactor
          compaction_interval: 10m
          retention_enabled: true
          retention_delete_delay: 2h
          retention_delete_worker_count: 150
          delete_request_store: s3
        ingester:
          chunk_target_size: 1572864
          chunk_encoding: snappy
          max_chunk_age: 2h
          chunk_idle_period: 90m
          chunk_retain_period: 30s
          autoforget_unhealthy: true
          lifecycler:
            ring:
              kvstore:
                store: inmemory
              replication_factor: 1
              heartbeat_timeout: 10m
          wal:
            enabled: false
        limits_config:
          discover_service_name: []
          retention_period: 48h
          reject_old_samples: false
          ingestion_rate_mb: 256
          ingestion_burst_size_mb: 256
          max_streams_per_user: 20000
          max_global_streams_per_user: 20000
          max_line_size: 0
          per_stream_rate_limit: 256MB
          per_stream_rate_limit_burst: 256MB
          shard_streams:
            enabled: false
            desired_rate: 3MB
        schema_config:
          configs:
          - from: "2020-10-24"
            index:
              period: 24h
              prefix: index_
            object_store: s3
            schema: v13
            store: tsdb
        server:
          http_listen_port: 3100
          grpc_server_max_recv_msg_size: 104857600
          grpc_server_max_send_msg_size: 104857600
          graceful_shutdown_timeout: 60s
        analytics:
          reporting_enabled: false
        storage_config:
          tsdb_shipper:
            active_index_directory: /tsdb/index
            cache_location: /tsdb/index-cache
            cache_ttl: 24h
          aws:
            endpoint: APPLIANCE_LOGS_BUCKET_ENDPOINT
            bucketnames: APPLIANCE_LOGS_BUCKET_FULLY_QUALIFIED_NAME
            access_key_id: ${S3_ACCESS_KEY_ID}
            secret_access_key: ${S3_SECRET_ACCESS_KEY}
            s3forcepathstyle: true
    ---
    EOF
    
  4. Loki statefulset 및 서비스를 만듭니다.

    kubectl apply -f - <<EOF
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      labels:
        app: STATEFULSET_NAME
      name: STATEFULSET_NAME
      namespace: obs-system
    spec:
      persistentVolumeClaimRetentionPolicy:
        whenDeleted: Retain
        whenScaled: Retain
      podManagementPolicy: OrderedReady
      replicas: 1
      revisionHistoryLimit: 10
      selector:
        matchLabels:
          app: STATEFULSET_NAME
      serviceName: STATEFULSET_NAME
      template:
        metadata:
          labels:
            app: STATEFULSET_NAME
            istio.io/rev: default
        spec:
          affinity:
            nodeAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - preference:
                  matchExpressions:
                  - key: node-role.kubernetes.io/control-plane
                    operator: DoesNotExist
                  - key: node-role.kubernetes.io/master
                    operator: DoesNotExist
                weight: 1
            podAntiAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - podAffinityTerm:
                  labelSelector:
                    matchExpressions:
                    - key: app
                      operator: In
                      values:
                      - STATEFULSET_NAME
                  topologyKey: kubernetes.io/hostname
                weight: 100
          containers:
          - args:
            - -config.file=/etc/loki/loki.yaml
            - -config.expand-env=true
            - -target=all
            env:
            - name: S3_ACCESS_KEY_ID
              valueFrom:
                secretKeyRef:
                  key: access-key-ID
                  name: APPLIANCE_LOGS_BUCKET_SECRET_NAME
                  optional: false
            - name: S3_SECRET_ACCESS_KEY
              valueFrom:
                  secretKeyRef:
                    key: secret-access-key
                    name: APPLIANCE_LOGS_BUCKET_SECRET_NAME
                    optional: false
            image: gcr.io/private-cloud-staging/loki:v3.0.1-gke.1
            imagePullPolicy: Always
            livenessProbe:
              failureThreshold: 3
              httpGet:
                path: /ready
                port: loki-server
                scheme: HTTP
              initialDelaySeconds: 330
              periodSeconds: 10
              successThreshold: 1
              timeoutSeconds: 1
            name: STATEFULSET_NAME
            ports:
            - containerPort: 3100
              name: loki-server
              protocol: TCP
            - containerPort: 7946
              name: gossip-ring
              protocol: TCP
            readinessProbe:
              failureThreshold: 3
              httpGet:
                path: /ready
                port: loki-server
                scheme: HTTP
              initialDelaySeconds: 45
              periodSeconds: 10
              successThreshold: 1
              timeoutSeconds: 1
            resources:
              limits:
                ephemeral-storage: 2000Mi
                memory: 8000Mi
              requests:
                cpu: 300m
                ephemeral-storage: 2000Mi
                memory: 1000Mi
            securityContext:
              readOnlyRootFilesystem: true
            terminationMessagePath: /dev/termination-log
            terminationMessagePolicy: File
            volumeMounts:
            - mountPath: /etc/loki
              name: config
            - mountPath: /data
              name: loki-storage
            - mountPath: /tsdb
              name: loki-tsdb-storage
            - mountPath: /tmp
              name: temp
            - mountPath: /tmp/loki/rules-temp
              name: tmprulepath
            - mountPath: /etc/ssl/certs
              name: trust-bundle
              readOnly: true
          dnsPolicy: ClusterFirst
          restartPolicy: Always
          schedulerName: default-scheduler
          securityContext:
            fsGroup: 10001
            runAsGroup: 10001
            runAsUser: 10001
          terminationGracePeriodSeconds: 4800
          volumes:
          - emptyDir: {}
            name: temp
          - emptyDir: {}
            name: tmprulepath
          - configMap:
              defaultMode: 420
              name: trust-store-root-ext
              optional: true
            name: trust-bundle
          - configMap:
              defaultMode: 420
              name: CONFIGMAP_NAME
            name: config
      updateStrategy:
        type: RollingUpdate
      volumeClaimTemplates:
      - apiVersion: v1
        kind: PersistentVolumeClaim
        metadata:
          creationTimestamp: null
          name: loki-storage
        spec:
          accessModes:
          - ReadWriteOnce
          resources:
            requests:
              storage: 5Gi
          storageClassName: standard-rwo
          volumeMode: Filesystem
      - apiVersion: v1
        kind: PersistentVolumeClaim
        metadata:
          creationTimestamp: null
          name: loki-tsdb-storage
        spec:
          accessModes:
          - ReadWriteOnce
          resources:
            requests:
              storage: 5Gi
          storageClassName: standard-rwo
          volumeMode: Filesystem
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: STATEFULSET_NAME
      namespace: obs-system
    spec:
      internalTrafficPolicy: Cluster
      ipFamilies:
      - IPv4
      ipFamilyPolicy: SingleStack
      ports:
      - name: loki-server
        port: 3100
        protocol: TCP
        targetPort: loki-server
      selector:
        app: STATEFULSET_NAME
      sessionAffinity: None
      type: ClusterIP
    ---
    EOF
    

Grafana 설정 DataSource

  1. KUBECONFIG를 Org Management API로 설정합니다.

    export KUBECONFIG=ORG_MANAGEMENT_API_KUBECONFIG_PATH
    
  2. 인프라 및 플랫폼 로그에 대해 DataSources를 만듭니다.

    kubectl apply -f - <<EOF
    ---
    apiVersion: monitoring.private.gdc.goog/v1alpha1
    kind: Datasource
    metadata:
      name: INFRA_DATASOURCE_NAME
      namespace: APPLIANCE_PROJECT_NAME-obs-system
    spec:
      datasource:
        access: proxy
        isDefault: false
        jsonData:
          httpHeaderName1: X-Scope-OrgID
        name: UI_FRIENDLY_NAME
        orgId: 1
        readOnly: true
        secureJsonData:
          httpHeaderValue1: infra-obs
        type: loki
        uid: INFRA_DATASOURCE_NAME
        url: http://STATEFULSET_NAME.obs-system.svc:3100
        version: 1
        withCredentials: false
    ---
    apiVersion: monitoring.private.gdc.goog/v1alpha1
    kind: Datasource
    metadata:
      name: PLATFORM_DATASOURCE_NAME
      namespace: APPLIANCE_PROJECT_NAME-obs-system
    spec:
      datasource:
        access: proxy
        isDefault: false
        jsonData:
          httpHeaderName1: X-Scope-OrgID
        name: UI_FRIENDLY_NAME
        orgId: 1
        readOnly: true
        secureJsonData:
          httpHeaderValue1: platform-obs
        type: loki
        uid: PLATFORM_DATASOURCE_NAME
        url: http://STATEFULSET_NAME.obs-system.svc:3100
        version: 1
        withCredentials: false
    ---
    EOF
    

Google Distributed Cloud(에어 갭 적용형) 데이터 센터 Grafana에서 로그 보기

Google Distributed Cloud 에어 갭 데이터 센터 버킷으로 내보낸 로그는 GDC 어플라이언스 프로젝트의 Grafana 인스턴스에서 볼 수 있습니다.