Nexus Kubernetes 클러스터 모니터링

각 Nexus Kubernetes 클러스터는 여러 계층으로 구성됩니다.

  • VM(Virtual Machines)
  • Kubernetes 레이어
  • 애플리케이션 Pod

Screenshot of Sample Nexus Kubernetes cluster.

그림: 샘플 Nexus Kubernetes 클러스터

인스턴스에서 Nexus Kubernetes 클러스터는 선택적 Container Insights 관찰 솔루션과 함께 제공됩니다. Container Insights는 Nexus Kubernetes 클러스터 및 워크로드에서 로그 및 메트릭을 캡처합니다. 이 도구를 사용할 것인지, 아니면 고유한 원격 분석 스택을 배포할 것인지는 전적으로 사용자의 재량입니다.

Azure 모니터링 도구를 사용하는 Nexus Kubernetes 클러스터는 다음과 같습니다.

Screenshot of Nexus Kubernetes cluster with Monitoring Tools.

그림: 모니터링 도구를 사용하는 Nexus Kubernetes 클러스터

관리 ID 인증을 사용하는 CLI로 확장 온보딩

Azure CLI를 시작하는 방법, 여러 운영 체제에서 CLI를 설치하는 방법 및 CLI 확장을 설치하는 방법에 대한 설명서입니다.

필요한 CLI 확장의 최신 버전을 설치합니다.

Nexus Kubernetes 클러스터 모니터링 - VM 계층

이 방법 가이드에서는 Arc에서 Nexus Kubernetes 클러스터 Virtual Machines를 Azure에 연결하고 Azure Monitoring Agent를 사용하여 이러한 VM에서 시스템 로그를 수집하기 위한 모니터링 에이전트를 사용하도록 설정하는 단계 및 유틸리티 스크립트를 제공합니다. 이 지침에서는 Log Analytics 작업 영역에 로그 데이터를 수집하도록 설정하는 방법을 자세히 다룹니다.

다음 리소스에서 제공하는 지원 기능은 다음과 같습니다.

  • arc-connect.env: 이 템플릿 파일을 사용하여 포함된 스크립트에 필요한 환경 변수를 만듭니다.

export SUBSCRIPTION_ID=""
export SERVICE_PRINCIPAL_ID=""
export SERVICE_PRINCIPAL_SECRET=""
export RESOURCE_GROUP=""
export TENANT_ID=""
export LOCATION=""
export INSTALL_AZURE_MONITOR_AGENT="true"
export PROXY_URL=""
export NAMESPACE=""
export AZURE_MONITOR_AGENT_VERSION="1.24.2"
export CONNECTEDMACHINE_AZCLI_VERSION="0.6.0"
  • dcr.sh: 이 스크립트를 사용하여 syslog 수집을 구성하기 위한 DCR(데이터 수집 규칙)을 만듭니다.

#!/bin/bash
set -e

SUBSCRIPTION_ID="${SUBSCRIPTION_ID:?SUBSCRIPTION_ID must be set}"
SERVICE_PRINCIPAL_ID="${SERVICE_PRINCIPAL_ID:?SERVICE_PRINCIPAL_ID must be set}"
SERVICE_PRINCIPAL_SECRET="${SERVICE_PRINCIPAL_SECRET:?SERVICE_PRINCIPAL_SECRET must be set}"
RESOURCE_GROUP="${RESOURCE_GROUP:?RESOURCE_GROUP must be set}"
TENANT_ID="${TENANT_ID:?TENANT_ID must be set}"
LOCATION="${LOCATION:?LOCATION must be set}"
LAW_RESOURCE_ID="${LAW_RESOURCE_ID:?LAW_RESOURCE_ID must be set}"
DCR_NAME=${DCR_NAME:-${RESOURCE_GROUP}-syslog-dcr}

az login --service-principal -u "${SERVICE_PRINCIPAL_ID}" -p "${SERVICE_PRINCIPAL_SECRET}" -t "${TENANT_ID}"

az account set -s "${SUBSCRIPTION_ID}"

az extension add --name monitor-control-service

RULEFILE=$(mktemp)
tee "${RULEFILE}" <<EOF
{
  "location": "${LOCATION}",
  "properties": {
    "dataSources": {
      "syslog": [
        {
          "name": "syslog",
          "streams": [
            "Microsoft-Syslog"
          ],
          "facilityNames": [
            "auth",
            "authpriv",
            "cron",
            "daemon",
            "mark",
            "kern",
            "local0",
            "local1",
            "local2",
            "local3",
            "local4",
            "local5",
            "local6",
            "local7",
            "lpr",
            "mail",
            "news",
            "syslog",
            "user",
            "uucp"
          ],
          "logLevels": [
            "Info",
            "Notice",
            "Warning",
            "Error",
            "Critical",
            "Alert",
            "Emergency"
          ]
        }
      ]
    },
    "destinations": {
      "logAnalytics": [
        {
          "workspaceResourceId": "${LAW_RESOURCE_ID}",
          "name": "centralWorkspace"
        }
      ]
    },
    "dataFlows": [
      {
        "streams": [
          "Microsoft-Syslog"
        ],
        "destinations": [
          "centralWorkspace"
        ]
      }
    ]
  }
}

EOF

az monitor data-collection rule create --name "${DCR_NAME}" --resource-group "${RESOURCE_GROUP}" --location "${LOCATION}" --rule-file "${RULEFILE}" -o tsv --query id

rm -rf "${RULEFILE}"
  • assign.sh: 스크립트를 사용하여 리소스 그룹의 모든 Arc 지원 서버와 DCR을 연결하기 위한 정책을 만듭니다.

#!/bin/bash
set -e

SUBSCRIPTION_ID="${SUBSCRIPTION_ID:?SUBSCRIPTION_ID must be set}"
SERVICE_PRINCIPAL_ID="${SERVICE_PRINCIPAL_ID:?SERVICE_PRINCIPAL_ID must be set}"
SERVICE_PRINCIPAL_SECRET="${SERVICE_PRINCIPAL_SECRET:?SERVICE_PRINCIPAL_SECRET must be set}"
RESOURCE_GROUP="${RESOURCE_GROUP:?RESOURCE_GROUP must be set}"
TENANT_ID="${TENANT_ID:?TENANT_ID must be set}"
LOCATION="${LOCATION:?LOCATION must be set}"
DCR_NAME=${DCR_NAME:-${RESOURCE_GROUP}-syslog-dcr}
POLICY_NAME=${POLICY_NAME:-${DCR_NAME}-policy}

az login --service-principal -u "${SERVICE_PRINCIPAL_ID}" -p "${SERVICE_PRINCIPAL_SECRET}" -t "${TENANT_ID}"

az account set -s "${SUBSCRIPTION_ID}"

DCR=$(az monitor data-collection rule show --name "${DCR_NAME}" --resource-group "${RESOURCE_GROUP}" -o tsv --query id)

PRINCIPAL=$(az policy assignment create \
  --name "${POLICY_NAME}" \
  --display-name "${POLICY_NAME}" \
  --resource-group "${RESOURCE_GROUP}" \
  --location "${LOCATION}" \
  --policy "d5c37ce1-5f52-4523-b949-f19bf945b73a" \
  --assign-identity \
  -p "{\"dcrResourceId\":{\"value\":\"${DCR}\"}}" \
  -o tsv --query identity.principalId)

required_roles=$(az policy definition show -n "d5c37ce1-5f52-4523-b949-f19bf945b73a" --query policyRule.then.details.roleDefinitionIds -o tsv)
for roleId in $(echo "$required_roles"); do
  az role assignment create \
    --role "${roleId##*/}" \
    --assignee-object-id "${PRINCIPAL}" \
    --assignee-principal-type "ServicePrincipal" \
    --scope /subscriptions/"$SUBSCRIPTION_ID"/resourceGroups/"$RESOURCE_GROUP"
done
  • install.sh: 각 VM에 Azure Monitoring Agent를 설치하여 Azure Virtual Machines에서 모니터링 데이터를 수집합니다.
#!/bin/bash
set -e

function create_secret() {
  kubectl apply -f - -n "${NAMESPACE}" <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: naks-vm-telemetry
type: Opaque
stringData:
  SUBSCRIPTION_ID: "${SUBSCRIPTION_ID}"
  SERVICE_PRINCIPAL_ID: "${SERVICE_PRINCIPAL_ID}"
  SERVICE_PRINCIPAL_SECRET: "${SERVICE_PRINCIPAL_SECRET}"
  RESOURCE_GROUP: "${RESOURCE_GROUP}"
  TENANT_ID: "${TENANT_ID}"
  LOCATION: "${LOCATION}"
  PROXY_URL: "${PROXY_URL}"
  INSTALL_AZURE_MONITOR_AGENT: "${INSTALL_AZURE_MONITOR_AGENT}"
  VERSION: "${AZURE_MONITOR_AGENT_VERSION}"
  CONNECTEDMACHINE_AZCLI_VERSION: "${CONNECTEDMACHINE_AZCLI_VERSION}"
EOF
}

function create_daemonset() {
  kubectl apply -f - -n "${NAMESPACE}" <<EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: naks-vm-telemetry
  labels:
    k8s-app: naks-vm-telemetry
spec:
  selector:
    matchLabels:
      name: naks-vm-telemetry
  template:
    metadata:
      labels:
        name: naks-vm-telemetry
    spec:
      hostNetwork: true
      hostPID: true
      containers:
        - name: naks-vm-telemetry
          image: mcr.microsoft.com/oss/mirror/docker.io/library/ubuntu:20.04
          env:
            - name: SUBSCRIPTION_ID
              valueFrom:
                secretKeyRef:
                  name: naks-vm-telemetry
                  key: SUBSCRIPTION_ID
            - name: SERVICE_PRINCIPAL_ID
              valueFrom:
                secretKeyRef:
                  name: naks-vm-telemetry
                  key: SERVICE_PRINCIPAL_ID
            - name: SERVICE_PRINCIPAL_SECRET
              valueFrom:
                secretKeyRef:
                  name: naks-vm-telemetry
                  key: SERVICE_PRINCIPAL_SECRET
            - name: RESOURCE_GROUP
              valueFrom:
                secretKeyRef:
                  name: naks-vm-telemetry
                  key: RESOURCE_GROUP
            - name: TENANT_ID
              valueFrom:
                secretKeyRef:
                  name: naks-vm-telemetry
                  key: TENANT_ID
            - name: LOCATION
              valueFrom:
                secretKeyRef:
                  name: naks-vm-telemetry
                  key: LOCATION
            - name: PROXY_URL
              valueFrom:
                secretKeyRef:
                  name: naks-vm-telemetry
                  key: PROXY_URL
            - name: INSTALL_AZURE_MONITOR_AGENT
              valueFrom:
                secretKeyRef:
                  name: naks-vm-telemetry
                  key: INSTALL_AZURE_MONITOR_AGENT
            - name: VERSION
              valueFrom:
                secretKeyRef:
                  name: naks-vm-telemetry
                  key: VERSION
            - name: CONNECTEDMACHINE_AZCLI_VERSION
              valueFrom:
                secretKeyRef:
                  name: naks-vm-telemetry
                  key: CONNECTEDMACHINE_AZCLI_VERSION
          securityContext:
            privileged: true
          command:
            - /bin/bash
            - -c
            - |
              set -e
              WORKDIR=\$(nsenter -t1 -m -u -n -i mktemp -d)
              trap 'nsenter -t1 -m -u -n -i rm -rf "\${WORKDIR}"; echo "Azure Monitor Configuration Failed"' ERR
              nsenter -t1 -m -u -n -i mkdir -p "\${WORKDIR}"/telemetry

              nsenter -t1 -m -u -n -i tee "\${WORKDIR}"/telemetry/telemetry_common.py > /dev/null <<EOF
              #!/usr/bin/python3
              import json
              import logging
              import os
              import socket
              import subprocess
              import sys

              arc_config_file = "\${WORKDIR}/telemetry/arc-connect.json"


              class AgentryResult:
                  CONNECTED = "Connected"
                  CREATING = "Creating"
                  DISCONNECTED = "Disconnected"
                  FAILED = "Failed"
                  SUCCEEDED = "Succeeded"


              class OnboardingMessage:
                  COMPLETED = "Onboarding completed"
                  STILL_CREATING = "Azure still creating"
                  STILL_TRYING = "Service still trying"


              def get_logger(logger_name):
                  logger = logging.getLogger(logger_name)
                  logger.setLevel(logging.DEBUG)
                  handler = logging.StreamHandler(stream=sys.stdout)
                  format = logging.Formatter(fmt="%(name)s - %(levelname)s - %(message)s")
                  handler.setFormatter(format)
                  logger.addHandler(handler)
                  return logger


              def az_cli_cm_ext_install(logger, config):
                  logger.info("Install az CLI connectedmachine extension")
                  proxy_url = config.get("PROXY_URL")
                  if proxy_url is not None:
                      os.environ["HTTP_PROXY"] = proxy_url
                      os.environ["HTTPS_PROXY"] = proxy_url
                  cm_azcli_version = config.get("CONNECTEDMACHINE_AZCLI_VERSION")
                  logger.info("Install az CLI connectedmachine extension: {cm_azcli_version}")
                  ext_cmd = f'/usr/bin/az extension add --name connectedmachine --version "{cm_azcli_version}" --yes'
                  run_cmd(logger, ext_cmd)


              def get_cm_properties(logger, config):
                  hostname = socket.gethostname()
                  resource_group = config.get("RESOURCE_GROUP")

                  logger.info(f"Getting arc enrollment properties for {hostname}...")

                  az_login(logger, config)

                  property_cmd = f'/usr/bin/az connectedmachine show --machine-name "{hostname}" --resource-group "{resource_group}"'

                  try:
                      raw_property = run_cmd(logger, property_cmd)
                      cm_json = json.loads(raw_property.stdout)
                      provisioning_state = cm_json["provisioningState"]
                      status = cm_json["status"]
                  except:
                      logger.warning("Connectedmachine not yet present")
                      provisioning_state = "NOT_PROVISIONED"
                      status = "NOT_CONNECTED"
                  finally:
                      az_logout(logger)

                  logger.info(
                      f'Connected machine "{hostname}" provisioningState is "{provisioning_state}" and status is "{status}"'
                  )

                  return provisioning_state, status


              def get_cm_extension_state(logger, config, extension_name):
                  resource_group = config.get("RESOURCE_GROUP")
                  hostname = socket.gethostname()

                  logger.info(f"Getting {extension_name} state for {hostname}...")

                  az_login(logger, config)

                  state_cmd = f'/usr/bin/az connectedmachine extension show --name "{extension_name}" --machine-name "{hostname}" --resource-group "{resource_group}"'

                  try:
                      raw_state = run_cmd(logger, state_cmd)
                      cme_json = json.loads(raw_state.stdout)
                      provisioning_state = cme_json["provisioningState"]
                  except:
                      logger.warning("Connectedmachine extension not yet present")
                      provisioning_state = "NOT_PROVISIONED"
                  finally:
                      az_logout(logger)

                  logger.info(
                      f'Connected machine "{hostname}" extenstion "{extension_name}" provisioningState is "{provisioning_state}"'
                  )

                  return provisioning_state


              def run_cmd(logger, cmd, check_result=True, echo_output=True):
                  res = subprocess.run(
                      cmd,
                      shell=True,
                      stdout=subprocess.PIPE,
                      stderr=subprocess.PIPE,
                      universal_newlines=True,
                  )

                  if res.stdout:
                      if echo_output:
                          logger.info(f"[OUT] {res.stdout}")

                  if res.stderr:
                      if echo_output:
                          logger.info(f"[ERR] {res.stderr}")

                  if check_result:
                      res.check_returncode()

                  return res  # can parse out res.stdout and res.returncode


              def az_login(logger, config):
                  logger.info("Login to Azure account...")
                  proxy_url = config.get("PROXY_URL")
                  if proxy_url is not None:
                      os.environ["HTTP_PROXY"] = proxy_url
                      os.environ["HTTPS_PROXY"] = proxy_url

                  service_principal_id = config.get("SERVICE_PRINCIPAL_ID")
                  service_principal_secret = config.get("SERVICE_PRINCIPAL_SECRET")
                  tenant_id = config.get("TENANT_ID")
                  subscription_id = config.get("SUBSCRIPTION_ID")
                  cmd = f'/usr/bin/az login --service-principal --username "{service_principal_id}" --password "{service_principal_secret}" --tenant "{tenant_id}"'
                  run_cmd(logger, cmd)
                  logger.info(f"Set Subscription...{subscription_id}")
                  set_sub = f'/usr/bin/az account set --subscription "{subscription_id}"'
                  run_cmd(logger, set_sub)


              def az_logout(logger):
                  logger.info("Logout of Azure account...")
                  run_cmd(logger, "/usr/bin/az logout --verbose", check_result=False)

              EOF

              nsenter -t1 -m -u -n -i tee "\${WORKDIR}"/telemetry/setup_azure_monitor_agent.py > /dev/null <<EOF
              #!/usr/bin/python3
              import json
              import os
              import socket
              import time

              import telemetry_common


              def run_install(logger, ama_config):
                  logger.info("Install Azure Monitor agent...")
                  resource_group = ama_config.get("RESOURCE_GROUP")
                  location = ama_config.get("LOCATION")
                  proxy_url = ama_config.get("PROXY_URL")
                  hostname = socket.gethostname()
                  if proxy_url is not None:
                      os.environ["HTTP_PROXY"] = proxy_url
                      os.environ["HTTPS_PROXY"] = proxy_url
                      settings = (
                          '{"proxy":{"mode":"application","address":"'
                          + proxy_url
                          + '","auth": "false"}}'
                      )
                      cmd = f'/usr/bin/az connectedmachine extension create --no-wait --name "AzureMonitorLinuxAgent" --publisher "Microsoft.Azure.Monitor" --type "AzureMonitorLinuxAgent" --machine-name "{hostname}" --resource-group "{resource_group}" --location "{location}" --verbose --settings \'{settings}\''
                  else:
                      cmd = f'/usr/bin/az connectedmachine extension create --no-wait --name "AzureMonitorLinuxAgent" --publisher "Microsoft.Azure.Monitor" --type "AzureMonitorLinuxAgent" --machine-name "{hostname}" --resource-group "{resource_group}" --location "{location}" --verbose'

                  version = ama_config.get("VERSION")
                  if version is not None:
                      cmd += f' --type-handler-version "{version}"'

                  logger.info("Installing Azure Monitor agent...")
                  telemetry_common.az_login(logger, ama_config)

                  try:
                      telemetry_common.run_cmd(logger, cmd)
                  except:
                      logger.info("Trying to install Azure Monitor agent...")
                  finally:
                      telemetry_common.az_logout(logger)


              def run_uninstall(logger, ama_config):
                  logger.info("Uninstall Azure Monitor agent...")
                  resource_group = ama_config.get("RESOURCE_GROUP")
                  hostname = socket.gethostname()
                  cmd = f'/usr/bin/az connectedmachine extension delete --name "AzureMonitorLinuxAgent" --machine-name "{hostname}" --resource-group "{resource_group}" --yes --verbose'

                  telemetry_common.az_login(logger, ama_config)
                  logger.info("Uninstalling Azure Monitor agent...")

                  try:
                      telemetry_common.run_cmd(logger, cmd)
                  except:
                      print("Trying to uninstall Azure Monitor agent...")
                  finally:
                      telemetry_common.az_logout(logger)


              def ama_installation(logger, ama_config):
                  logger.info("Executing AMA extenstion installation...")
                  telemetry_common.az_cli_cm_ext_install(logger, ama_config)

                  # Get connected machine properties
                  cm_provisioning_state, cm_status = telemetry_common.get_cm_properties(
                    logger, ama_config
                  )

                  if (
                    cm_provisioning_state == telemetry_common.AgentryResult.SUCCEEDED
                    and cm_status == telemetry_common.AgentryResult.CONNECTED
                  ):
                    # Get AzureMonitorLinuxAgent extension status
                    ext_provisioning_state = telemetry_common.get_cm_extension_state(
                      logger, ama_config, "AzureMonitorLinuxAgent"
                    )
  
                    if ext_provisioning_state == telemetry_common.AgentryResult.SUCCEEDED:
                      logger.info(telemetry_common.OnboardingMessage.COMPLETED)
                      return True
                    elif ext_provisioning_state == telemetry_common.AgentryResult.FAILED:
                      run_uninstall(logger, ama_config)
                      logger.warning(telemetry_common.OnboardingMessage.STILL_TRYING)
                      return False
                    elif ext_provisioning_state == telemetry_common.AgentryResult.CREATING:
                      logger.warning(telemetry_common.OnboardingMessage.STILL_CREATING)
                      return False
                    else:
                      run_install(logger, ama_config)
                      logger.warning(telemetry_common.OnboardingMessage.STILL_TRYING)
                      return False
                  else:
                    logger.error("Server not arc enrolled, enroll the server and retry")
                    return False


              def main():
                  timeout = 60  # TODO: increase when executed via systemd unit
                  start_time = time.time()
                  end_time = start_time + timeout

                  config_file = telemetry_common.arc_config_file

                  logger = telemetry_common.get_logger(__name__)

                  logger.info("Running setup_azure_monitor_agent.py...")

                  if config_file is None:
                      raise Exception("config file is expected")

                  ama_config = {}

                  with open(config_file, "r") as file:
                      ama_config = json.load(file)

                  ama_installed = False

                  while time.time() < end_time:
                      logger.info("Installing AMA extension...")
                      try:
                          ama_installed = ama_installation(logger, ama_config)
                      except Exception as e:
                          logger.error(f"Could not install AMA extension: {e}")
                      if ama_installed:
                          break
                      logger.info("Sleeping 30s...")  # retry for Azure info
                      time.sleep(30)


              if __name__ == "__main__":
                  main()

              EOF


              nsenter -t1 -m -u -n -i tee "\${WORKDIR}"/arc-connect.sh > /dev/null <<EOF
              #!/bin/bash
              set -e

              echo "{\"VERSION\": \"\${VERSION}\", \"SUBSCRIPTION_ID\": \"\${SUBSCRIPTION_ID}\", \"SERVICE_PRINCIPAL_ID\": \"\${SERVICE_PRINCIPAL_ID}\", \"SERVICE_PRINCIPAL_SECRET\": \"\${SERVICE_PRINCIPAL_SECRET}\", \"RESOURCE_GROUP\": \"\${RESOURCE_GROUP}\", \"TENANT_ID\": \"\${TENANT_ID}\", \"LOCATION\": \"\${LOCATION}\", \"PROXY_URL\": \"\${PROXY_URL}\", \"CONNECTEDMACHINE_AZCLI_VERSION\": \"\${CONNECTEDMACHINE_AZCLI_VERSION}\"}" > "\${WORKDIR}"/telemetry/arc-connect.json

              if [ "\${INSTALL_AZURE_MONITOR_AGENT}" = "true" ]; then
                echo "Installing Azure Monitor agent..."
                /usr/bin/python3 "\${WORKDIR}"/telemetry/setup_azure_monitor_agent.py > "\${WORKDIR}"/setup_azure_monitor_agent.out
                cat "\${WORKDIR}"/setup_azure_monitor_agent.out
                if grep "Could not install AMA extension" "\${WORKDIR}"/setup_azure_monitor_agent.out > /dev/null; then
                  exit 1
                fi
              fi
              EOF

              nsenter -t1 -m -u -n -i sh "\${WORKDIR}"/arc-connect.sh
              nsenter -t1 -m -u -n -i rm -rf "\${WORKDIR}"
              echo "Server monitoring configured successfully"
              tail -f /dev/null
          livenessProbe:
            initialDelaySeconds: 600
            periodSeconds: 60
            timeoutSeconds: 30
            exec:
              command:
                - /bin/bash
                - -c
                - |
                  set -e
                  WORKDIR=\$(nsenter -t1 -m -u -n -i mktemp -d)
                  trap 'nsenter -t1 -m -u -n -i rm -rf "\${WORKDIR}"' ERR EXIT
                  nsenter -t1 -m -u -n -i tee "\${WORKDIR}"/liveness.sh > /dev/null <<EOF
                  #!/bin/bash
                  set -e

                  # Check AMA processes
                  ps -ef | grep "\\\s/opt/microsoft/azuremonitoragent/bin/agentlauncher\\\s"
                  ps -ef | grep "\\\s/opt/microsoft/azuremonitoragent/bin/mdsd\\\s"
                  ps -ef | grep "\\\s/opt/microsoft/azuremonitoragent/bin/amacoreagent\\\s"

                  # Check Arc server agent is Connected
                  AGENTSTATUS="\\\$(azcmagent show -j)"
                  if [[ \\\$(echo "\\\${AGENTSTATUS}" | jq -r .status) != "Connected" ]]; then
                    echo "azcmagent is not connected"
                    echo "\\\${AGENTSTATUS}"
                    exit 1
                  fi

                  # Verify dependent services are running
                  while IFS= read -r status; do
                    if [[ "\\\${status}" != "active" ]]; then
                      echo "one or more azcmagent services not active"
                      echo "\\\${AGENTSTATUS}"
                      exit 1
                    fi
                  done < <(jq -r '.services[] | (.status)' <<<\\\${AGENTSTATUS})

                  # Run connectivity tests
                  RESULT="\\\$(azcmagent check -j)"
                  while IFS= read -r reachable; do
                    if [[ ! \\\${reachable} ]]; then
                      echo "one or more connectivity tests failed"
                      echo "\\\${RESULT}"
                      exit 1
                    fi
                  done < <(jq -r '.[] | (.reachable)' <<<\\\${RESULT})

                  EOF

                  nsenter -t1 -m -u -n -i sh "\${WORKDIR}"/liveness.sh
                  nsenter -t1 -m -u -n -i rm -rf "\${WORKDIR}"
                  echo "Liveness check succeeded"

      tolerations:
        - operator: "Exists"
          effect: "NoSchedule"

EOF
}

SUBSCRIPTION_ID="${SUBSCRIPTION_ID:?SUBSCRIPTION_ID must be set}"
SERVICE_PRINCIPAL_ID="${SERVICE_PRINCIPAL_ID:?SERVICE_PRINCIPAL_ID must be set}"
SERVICE_PRINCIPAL_SECRET="${SERVICE_PRINCIPAL_SECRET:?SERVICE_PRINCIPAL_SECRET must be set}"
RESOURCE_GROUP="${RESOURCE_GROUP:?RESOURCE_GROUP must be set}"
TENANT_ID="${TENANT_ID:?TENANT_ID must be set}"
LOCATION="${LOCATION:?LOCATION must be set}"
PROXY_URL="${PROXY_URL:?PROXY_URL must be set}"
INSTALL_AZURE_MONITOR_AGENT="${INSTALL_AZURE_MONITOR_AGENT:?INSTALL_AZURE_MONITOR_AGENT must be true/false}"
NAMESPACE="${NAMESPACE:?NAMESPACE must be set}"
AZURE_MONITOR_AGENT_VERSION="${AZURE_MONITOR_AGENT_VERSION:-"1.24.2"}"
CONNECTEDMACHINE_AZCLI_VERSION="${CONNECTEDMACHINE_AZCLI_VERSION:-"0.6.0"}"

create_secret
create_daemonset

필수 구성 요소-VM

  • Nexus Kubernetes 클러스터에 대한 클러스터 관리자 액세스

  • Azure Arc 지원 서버를 사용하려면 다음 Azure 리소스 공급자를 구독에 등록합니다.

    • Microsoft.HybridCompute
    • Microsoft.GuestConfiguration
    • Microsoft.HybridConnectivity

이전에 등록하지 않은 경우 다음 리소스 공급자를 등록합니다.

az account set --subscription "{the Subscription Name}"
az provider register --namespace 'Microsoft.HybridCompute'
az provider register --namespace 'Microsoft.GuestConfiguration'
az provider register --namespace 'Microsoft.HybridConnectivity'
  • 필요에 따라 다음과 같은 Azure 기본 제공 역할에 Azure 서비스 주체를 할당합니다. 연결할 컴퓨터가 있는 Azure 리소스 그룹에 서비스 주체를 할당합니다.
역할 필요한 용도
Azure Connected Machine 리소스 관리자 또는 기여자 리소스 그룹에서 Arc 지원 Nexus Kubernetes 클러스터 VM 서버를 커넥트 AMA(Azure Monitoring Agent)를 설치합니다.
모니터링 기여자 또는 기여자 리소스 그룹에 DCR(데이터 수집 규칙)을 만들고 여기에 Arc 지원 서버를 연결합니다.
사용자 액세스 관리자리소스 정책 기여자 또는 기여자 Azure Policy 할당을 사용하여 DCR이 Arc 지원 컴퓨터와 연결되어 있는지 확인하려는 경우 필요
Kubernetes 확장 기여자 Container Insights용 K8s 확장을 배포하는 데 필요

환경 설정

포함된 스크립트를 복사하고 실행합니다. Azure Portal의 Azure Cloud Shell에서 실행할 수 있습니다. 또는 Kubernetes 명령줄 도구(kubectl) 및 Azure CLI가 설치된 Linux 명령 프롬프트에서 실행할 수 있습니다.

포함된 스크립트를 실행하기 전에 다음 환경 변수를 정의합니다.

환경 변수 설명
SUBSCRIPTION_ID 리소스 그룹이 포함된 Azure 구독의 ID
RESOURCE_GROUP Arc 지원 서버 및 연결된 리소스가 만들어지는 리소스 그룹 이름
위치 Arc 지원 서버 및 연결된 리소스가 만들어지는 Azure 지역
SERVICE_PRINCIPAL_ID 적절한 역할이 할당된 Azure 서비스 주체의 appId
SERVICE_PRINCIPAL_SECRET Azure 서비스 주체의 인증 암호
TENANT_ID 서비스 주체가 있는 테넌트 디렉터리의 ID
PROXY_URL Azure 서비스에 연결하는 데 사용할 프록시 URL
네임스페이스 Kubernetes 아티팩트가 만들어지는 네임스페이스

편의를 도모하기 위해 arc-connect.env 템플릿 파일을 수정하여 환경 변수 값을 설정할 수 있습니다.

# Apply the modified values to the environment
 ./arc-connect.env

DCR(데이터 수집 규칙) 추가

Arc 지원 서버를 DCR과 연결하여 Log Analytics 작업 영역에 로그 데이터를 수집할 수 있습니다. Azure Portal 또는 CLI를 통해 DCR을 만들 수 있습니다. VM에서 데이터를 수집하는 DCR 만들기에 대한 정보는 여기에서 확인할 수 있습니다.

포함된 dcr.sh 스크립트는 지정된 리소스 그룹에 로그 수집을 구성할 DCR을 만듭니다.

  1. 서비스 주체에 적절한 환경 설정 및 역할 필수 구성 요소를 확인합니다. DCR은 지정된 리소스 그룹에 만들어집니다.

  2. DCR에 따라 로그 데이터 수집용 Log Analytics 작업 영역을 만들거나 식별합니다. 환경 변수 LAW_RESOURCE_ID를 해당 리소스 ID로 설정합니다. 알려진 Log Analytics 작업 영역 이름에 대한 리소스 ID를 검색합니다.

export LAW_RESOURCE_ID=$(az monitor log-analytics workspace show -g "${RESOURCE_GROUP}" -n <law name> --query id -o tsv)
  1. dcr.sh 스크립트를 실행합니다. 지정된 리소스 그룹에 이름이 ${RESOURCE_GROUP}-syslog-dcr인 DCR을 만듭니다.
./dcr.sh

Azure Portal 또는 CLI에서 DCR을 확인/관리합니다. 기본적으로 Linux Syslog 로그 수준은 'INFO'로 설정됩니다. 필요에 따라 로그 수준을 변경할 수 있습니다.

참고 항목

DCR을 만들기 전에 수동으로 또는 정책을 통해 만든 서버를 연결합니다. 수정 작업을 참조하세요.

DCR에 Arc 지원 서버 리소스 연결

로그가 Log Analytics 작업 영역으로 전달되도록 만든 DCR에 Arc 지원 서버 리소스를 연결합니다. DCR과 서버를 연결하는 옵션이 있습니다.

Azure Portal 또는 CLI를 사용하여 DCR에 선택한 Arc 지원 서버 연결

Azure Portal에서 리소스 섹션을 사용하여 Arc 지원 서버 리소스를 DCR에 추가합니다.

Azure CLI를 통해 리소스를 연결하는 방법에 대한 자세한 내용은 이 링크를 사용하세요.

Azure Policy를 사용하여 DCR 연결 관리

리소스 그룹에 정책을 할당하여 연결을 적용합니다. Linux Arc 머신을 DCR과 연결하는 기본 제공 정책 정의가 있습니다. DCR을 매개 변수로 사용하여 리소스 그룹에 정책을 할당합니다. 리소스 그룹 내에 있는 모든 Arc 지원 서버가 동일한 DCR와 연결되도록 합니다.

Azure Portal의 정책 정의 페이지에서 Assign 단추를 선택합니다.

편의를 도모하기 위해 제공된 assign.sh 스크립트는 dcr.sh 스크립트로 만든 지정된 리소스 그룹과 DCR에 기본 제공 정책을 할당합니다.

  1. 서비스 주체가 정책 및 역할 할당을 수행하는 데 적절한 환경 설정 및 역할 필수 구성 요소를 확인합니다.
  2. 데이터 수집 규칙 추가 섹션에 설명된 대로 dcr.sh 스크립트를 사용하여 리소스 그룹에 DCR을 만듭니다.
  3. assign.sh 스크립트를 실행합니다. 정책 할당 및 필요한 역할 할당을 만듭니다.
./assign.sh

Azure 모니터링 에이전트 설치

Nexus Kubernetes 클러스터에 Kubernetes daemonSet을 만드는 포함 install.sh 을 사용합니다. 각 클러스터 노드에 Pod를 배포하고 AMA(Azure Monitoring Agent)를 설치합니다. daemonSet에는 서버 연결 및 AMA 프로세스를 모니터링하는 활동성 프로브도 포함됩니다.

참고 항목

Azure Monitoring Agent를 설치하려면 먼저 Arc에서 Nexus Kubernetes 클러스터 VM을 연결해야 합니다. 이 프로세스는 최신 버전 번들을 사용하는 경우 자동화됩니다. 그러나 사용하는 버전 번들이 기본적으로 클러스터 VM Arc 등록을 지원하지 않는 경우 클러스터를 최신 버전 번들로 업그레이드해야 합니다. 버전 번들에 대한 자세한 내용은 Nexus Kubernetes 클러스터 지원 버전을 참조 하세요.

  1. 환경 설정에 지정된 대로 환경을 설정합니다. Nexus Kubernetes 클러스터 VM에 대한 현재 kubeconfig 컨텍스트를 설정합니다.
  2. Nexus Kubernetes 클러스터에 대한 액세스를 허용 Kubectl 합니다.

    참고 항목

    Nexus Kubernetes 클러스터를 만들면 Nexus는 클러스터 리소스 저장 전용 관리형 리소스 그룹을 자동으로 만들며, 이 그룹 내에서 Arc 연결 클러스터 리소스가 설정됩니다.

    클러스터에 액세스하려면 클러스터 연결 kubeconfig를 설정해야 합니다. 관련 Microsoft Entra 엔터티를 사용하여 Azure CLI에 로그인하면 클러스터를 둘러싸고 있는 방화벽 외부에서도 어디에서나 클러스터와 통신하는 데 필요한 kubeconfig를 가져올 수 있습니다.
    1. CLUSTER_NAME, RESOURCE_GROUPSUBSCRIPTION_ID 변수를 설정합니다.

      CLUSTER_NAME="myNexusK8sCluster"
      RESOURCE_GROUP="myResourceGroup"
      SUBSCRIPTION_ID=<set the correct subscription_id>
      
    2. az를 사용하여 관리형 리소스 그룹을 쿼리하고 MANAGED_RESOURCE_GROUP에 저장합니다.

       az account set -s $SUBSCRIPTION_ID
       MANAGED_RESOURCE_GROUP=$(az networkcloud kubernetescluster show -n $CLUSTER_NAME -g $RESOURCE_GROUP --output tsv --query managedResourceGroupConfiguration.name)
      
    3. 다음 명령은 지정된 Nexus Kubernetes 클러스터에 대한 Kubernetes API 서버에 연결할 수 있게 해주는 connectedk8s 프록시를 시작합니다.

      az connectedk8s proxy -n $CLUSTER_NAME  -g $MANAGED_RESOURCE_GROUP &
      
    4. kubectl을 사용하여 클러스터로 요청을 보냅니다.

      kubectl get pods -A
      

      이제 모든 노드 목록이 포함된 클러스터의 응답이 표시됩니다.

    참고 항목

    "클라이언트 프록시에 액세스 토큰을 게시하지 못했습니다. MSI에 연결하지 못함" 오류 메시지가 표시되면 az login을 수행하여 Azure에 다시 인증해야 할 수 있습니다.

  3. install.sh Nexus Kubernetes 클러스터에 대한 kubectl 액세스를 사용하여 명령 프롬프트에서 스크립트를 실행합니다.

스크립트는 클러스터에 daemonSet를 배포합니다. 다음과 같이 진행률을 모니터링합니다.

# Run the install script and observe results
./install.sh
kubectl get pod --selector='name=naks-vm-telemetry'
kubectl logs <podname>

완료되면 시스템에서 "서버 모니터링이 성공적으로 구성되었습니다."라는 메시지를 기록합니다.

참고 항목

이렇게 연결된 서버를 DCR에 연결합니다. 정책을 구성한 후 Azure Log Analytics 작업 영역에서 로그를 확인하는 데 시간이 약간 지연될 수 있습니다.

Nexus Kubernetes 클러스터 모니터링 – K8s 계층

필수 구성 요소-Kubernetes

운영자가 Nexus Kubernetes 클러스터에서 모니터링 도구를 구성해야 하는 특정 필수 구성 요소가 있습니다.

Container Insights는 Log Analytics 작업 영역에 데이터를 저장합니다. 로그 데이터는 'DCR(데이터 수집 규칙) 추가' 섹션에 나와 있는 초기 스크립트 과정에서 제공한 리소스 ID가 있는 작업 영역으로 이동합니다. 그렇지 않은 경우 데이터는 Azure 위치를 기준으로 구독과 연결된 리소스 그룹의 기본 작업 영역으로 이동합니다.

미국 동부를 예로 들면 다음과 같습니다.

  • Log Analytics 작업 영역 이름: DefaultWorkspace-<GUID>-EUS
  • 리소스 그룹 이름: DefaultResourceGroup-EUS

다음 명령을 실행하여 기존의 Log Analytics 작업 영역 리소스 ID를 가져옵니다.

az login

az account set --subscription "<Subscription Name or ID the Log Analytics workspace is in>"

az monitor log-analytics workspace show --workspace-name "<Log Analytics workspace Name>" \
  --resource-group "<Log Analytics workspace Resource Group>" \
  -o tsv --query id

Container Insights를 배포하고 해당 Log Analytics 작업 영역에서 데이터를 보려면 계정에 특정 역할을 할당해야 합니다. 예를 들어, '기여자' 역할을 할당합니다. 필요한 역할을 할당하기 위한 지침을 참조하세요.

  • Log Analytics 기여자 역할: CNF(프로비전된) 클러스터에서 컨테이너 모니터링을 사용하도록 설정하려면 필요한 권한입니다.
  • Log Analytics 읽기 권한자 역할: Log Analytics 기여자 역할의 멤버가 아닌 경우 컨테이너 모니터링을 사용하도록 설정하면 Log Analytics 작업 영역에서 데이터를 볼 수 있는 권한을 받아야 합니다.

클러스터 확장 설치

Azure Cloud Shell에 로그인하여 클러스터에 액세스합니다.

az login

az account set --subscription "<Subscription Name or ID the Provisioned Cluster is in>"

이제 다음 두 명령 중 하나를 사용하여 프로비전된 Nexus Kubernetes 클러스터에 Container Insights 확장을 배포합니다.

고객이 미리 만든 Log Analytics 작업 영역 사용

az k8s-extension create --name azuremonitor-containers \
  --cluster-name "<Nexus Kubernetes cluster Name>" \
  --resource-group "<Nexus Kubernetes cluster Resource Group>" \
  --cluster-type connectedClusters \
  --extension-type Microsoft.AzureMonitor.Containers \
  --release-train preview \
  --configuration-settings logAnalyticsWorkspaceResourceID="<Log Analytics workspace Resource ID>" \
  amalogsagent.useAADAuth=true

기본 Log Analytics 작업 영역 사용

az k8s-extension create --name azuremonitor-containers \
  --cluster-name "<Nexus Kubernetes cluster Name>" \
  --resource-group "<Nexus Kubernetes cluster Resource Group>" \
  --cluster-type connectedClusters \
  --extension-type Microsoft.AzureMonitor.Containers \
  --release-train preview \
  --configuration-settings amalogsagent.useAADAuth=true

클러스터 확장 확인

다음 명령을 사용하여 Nexus Kubernetes 클러스터에서 모니터링 에이전트의 사용 설정이 성공적으로 배포되었는지 확인합니다.

az k8s-extension show --name azuremonitor-containers \
  --cluster-name "<Nexus Kubernetes cluster Name>" \
  --resource-group "<Nexus Kubernetes cluster Resource Group>" \
  --cluster-type connectedClusters

확장의 프로비저닝 상태가 '성공'인지 확인합니다. 'k8s-extension create' 명령도 상태를 반환했을 수 있습니다.

로그 및 메트릭 컬렉션 사용자 지정

Container Insights는 Nexus Kubernetes 클러스터의 로그 및 메트릭 컬렉션을 미세 조정하는 최종 사용자 기능을 제공합니다. 컨테이너 인사이트 에이전트 데이터 수집 구성.

추가 리소스

  • 통합 문서 설명서를 검토한 다음 Operator Nexus 원격 분석 샘플 Operator Nexus 통합 문서를 사용할 수 있습니다.
  • Azure Monitor 경고, Azure Monitor 경고 규칙을 만드는 방법 및 샘플 운영자 Nexus 경고 템플릿을 사용하는 방법을 검토합니다.