共用方式為


Nexus Kubernetes 叢集概觀

每個 Nexus Kubernetes 叢集都包含多個層:

  • 虛擬機器 (VM)
  • Kubernetes 層
  • 應用程式 Pod

Screenshot of Sample Nexus Kubernetes cluster.

圖:範例 Nexus Kubernetes 叢集

在執行個體上,Nexus Kubernetes 叢集會透過「選擇性」容器深入解析可檢視性解決方案來傳遞。 容器深入解析會從 Nexus Kubernetes 叢集和工作負載擷取記錄和計量。 要啟用此工具或是部署您自己的遙測堆疊完全取決於您。

具有 Azure 監視工具的 Nexus Kubernetes 叢集如下所示:

Screenshot of Nexus Kubernetes cluster with Monitoring Tools.

圖:Nexus Kubernetes 叢集與監視工具

使用受控識別驗證透過 CLI 上線的延伸模組

Azure CLI 開始的文件,如何透過多個作業系統安裝,以及如何安裝 CLI 延伸模組

安裝最新版的必要 CLI 延伸模組

監視 Nexus Kubernetes 叢集 – VM 層

本操作指南提供步驟和公用程式指令碼將 Nexus Kubernetes 叢集虛擬機器 Arc 連線 至 Azure,並啟用監視代理程式,以使用 Azure 監視代理程式從這些 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:使用此指令碼建立資料收集規則 (DCR) 來設定 syslog 收集

#!/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:使用指令碼來建立原則,將 DCR 關聯至資源群組中所有已啟用 Arc 的伺服器

#!/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:已啟用 Arc 的 Nexus Kubernetes 叢集 VM,並在每個 VM 上安裝 Azure 監視代理程式
#!/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_arc_for_servers.py > /dev/null <<EOF
              #!/usr/bin/python3
              import json
              import time

              import telemetry_common


              def run_connect(logger, arc_config):
                  logger.info("Connect machine to Azure Arc...")
                  service_principal_id = arc_config.get("SERVICE_PRINCIPAL_ID")
                  service_principal_secret = arc_config.get("SERVICE_PRINCIPAL_SECRET")
                  resource_group = arc_config.get("RESOURCE_GROUP")
                  tenant_id = arc_config.get("TENANT_ID")
                  subscription_id = arc_config.get("SUBSCRIPTION_ID")
                  location = arc_config.get("LOCATION")
                  connect_cmd = f'azcmagent connect --service-principal-id "{service_principal_id}" --service-principal-secret "{service_principal_secret}" --resource-group "{resource_group}" --tenant-id "{tenant_id}" --subscription-id "{subscription_id}" --location "{location}"'

                  cloudtype = arc_config.get("CLOUDTYPE")
                  if cloudtype is not None:
                      connect_cmd += f' --cloud "{cloudtype}"'

                  tags = arc_config.get("TAGS")
                  if tags is not None:
                      connect_cmd += f' --tags "{tags}"'

                  correlation_id = arc_config.get("CORRELATION_ID")
                  if correlation_id is not None:
                      connect_cmd += f' --correlation-id "{correlation_id}"'

                  proxy_url = arc_config.get("PROXY_URL")
                  if proxy_url is not None:
                      set_proxy_cmd = f'/usr/bin/azcmagent config set proxy.url "{proxy_url}"'
                      telemetry_common.run_cmd(logger, set_proxy_cmd)

                  # Hardcoding allowed extensions as these will only vary if NC team adds new extensions
                  allowed_extensions = "Microsoft.Azure.Monitor/AzureMonitorLinuxAgent,Microsoft.Azure.AzureDefenderForServers/MDE.Linux"
                  set_extensions_cmd = (
                      f'/usr/bin/azcmagent config set extensions.allowlist "{allowed_extensions}"'
                  )
                  telemetry_common.run_cmd(logger, set_extensions_cmd)

                  telemetry_common.az_login(logger, arc_config)
                  logger.info("Connecting machine to Azure Arc...")

                  try:
                      telemetry_common.run_cmd(logger, connect_cmd)
                  except:
                      logger.info("Trying to connect machine to Azure Arc...")
                  finally:
                      telemetry_common.az_logout(logger)


              def run_disconnect(logger, arc_config):
                  logger.info("Disconnect machine from Azure Arc...")
                  service_principal_id = arc_config.get("SERVICE_PRINCIPAL_ID")
                  service_principal_secret = arc_config.get("SERVICE_PRINCIPAL_SECRET")

                  cmd = f'/usr/bin/azcmagent disconnect --service-principal-id "{service_principal_id}" --service-principal-secret "{service_principal_secret}"'

                  telemetry_common.az_login(logger, arc_config)

                  try:
                      telemetry_common.run_cmd(logger, cmd)
                  except:
                      logger.info("Trying to disconnect machine from Azure Arc...")
                  finally:
                      telemetry_common.az_logout(logger)


              def arc_enrollment(logger, arc_config):
                  logger.info("Executing Arc enrollment...")

                  telemetry_common.az_cli_cm_ext_install(logger, arc_config)

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

                  if (
                      cm_provisioning_state == telemetry_common.AgentryResult.SUCCEEDED
                      and cm_status == telemetry_common.AgentryResult.CONNECTED
                  ):
                      logger.info(telemetry_common.OnboardingMessage.COMPLETED)
                      return True
                  elif cm_provisioning_state in [
                      telemetry_common.AgentryResult.FAILED,
                      telemetry_common.AgentryResult.DISCONNECTED,
                  ]:
                      run_disconnect(logger, arc_config)
                      logger.warning(telemetry_common.OnboardingMessage.STILL_TRYING)
                      return False
                  elif cm_provisioning_state == telemetry_common.AgentryResult.CREATING:
                      logger.warning(telemetry_common.OnboardingMessage.STILL_CREATING)
                      return False
                  else:
                      run_connect(logger, arc_config)
                      logger.warning(telemetry_common.OnboardingMessage.STILL_TRYING)
                      return False


              def main():
                  timeout = 300  # 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_arc_for_servers.py...")

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

                  arc_config = {}

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

                  arc_enrolled = False

                  while time.time() < end_time:
                      logger.info("Arc enrolling the server...")
                      try:
                          arc_enrolled = arc_enrollment(logger, arc_config)
                      except Exception as e:
                          logger.error(f"Could not arc enroll server: {e}")
                      if arc_enrolled:
                          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}"/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

              echo "Connecting machine to Azure Arc..."
              /usr/bin/python3 "\${WORKDIR}"/telemetry/setup_arc_for_servers.py > "\${WORKDIR}"/setup_arc_for_servers.out
              cat "\${WORKDIR}"/setup_arc_for_servers.out
              if grep "Could not arc enroll server" "\${WORKDIR}"/setup_arc_for_servers.out > /dev/null; then
                exit 1
              fi

              /usr/bin/azcmagent config set incomingconnections.ports 22
              /usr/bin/azcmagent config set extensions.allowlist Microsoft.Azure.Monitor/AzureMonitorLinuxAgent,Microsoft.Azure.AzureDefenderForServers/MDE.Linux,Microsoft.Azure.ActiveDirectory/AADSSHLoginForLinux

              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 伺服器,並安裝 Azure 監視代理程式 (AMA)
監視參與者參與者 在資源群組中建立資料收集規則 (DCR),並將已啟用 Arc 的伺服器關聯至規則
使用者存取系統管理員,以及資源原則參與者參與者 如果您想要使用 Azure 原則指派來確定 DCR 已與已啟用 Arc 的電腦產生關聯,則為必要項目
Kube 延伸模組參與者 針對容器深入解析部署 K8s 延伸模組的必要項目

環境設定

複製並執行內含指令碼。 您可以在 Azure 入口網站中從 Azure Cloud Shell 執行。 或者您可以從已安裝 Kubernetes 命令列工具 (kubectl) 和 Azure CLI 的 Linux 命令提示字元執行。

在執行內含指令碼之前,請先定義下列環境變數:

環境變數 描述
SUBSCRIPTION_ID 包含資源群組的 Azure 訂用帳戶識別碼
RESOURCE_GROUP 已在其中建立已啟用 Arc 的伺服器和相關聯資源的資源群組名稱
LOCATION 已在其中建立已啟用 Arc 的伺服器和相關聯資源的 Azure 區域
SERVICE_PRINCIPAL_ID 具有適當角色指派的 Azure 服務主體 appId
SERVICE_PRINCIPAL_SECRET Azure 服務主體的驗證密碼
TENANT_ID 服務主體所在的租用戶目錄識別碼
PROXY_URL 用來連線到 Azure 服務的 Proxy URL
命名空間 在其中建立 Kubernetes 成品的命名空間

為了方便起見,您可以修改範本檔案 arc-connect.env,以設定環境變數值。

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

新增資料收集規則 (DCR)

將已啟用 Arc 的伺服器與 DCR 產生關聯,以將記錄資料收集到 Log Analytics 工作區。 您可以透過 Azure 入口網站或 CLI 建立 DCR。 如需建立 DCR 以從 VM 收集資料的詳細資訊,請參閱這裡

內含的 dcr.sh 指令碼會在指定的資源群組中建立 DCR,用於設定記錄收集。

  1. 請確定服務主體有適當的環境設定和角色必要條件。 DCR 會在指定的資源群組中建立。

  2. 根據 DCR 建立或識別記錄資料擷取的 Log Analytics 工作區。 將環境變數 LAW_RESOURCE_ID 設定為其資源識別碼。 擷取已知 Log Analytics 工作區名稱的資源識別碼:

export LAW_RESOURCE_ID=$(az monitor log-analytics workspace show -g "${RESOURCE_GROUP}" -n <law name> --query id -o tsv)
  1. 執行 dcr.sh 指令碼。 該指令碼會在指定的資源群組中建立 DCR,名稱為 ${RESOURCE_GROUP}-syslog-dcr
./dcr.sh

從 Azure 入口網站或 CLI 檢視/管理 DCR。 根據預設,Linux Syslog 記錄層級會設定為「INFO」。 您可以視需要變更記錄層級。

注意

在建立 DCR 之前,手動或透過原則與已建立伺服器產生關聯。 請參閱補救工作

將已啟用 Arc 的伺服器資源關聯至 DCR

將已啟用 Arc 的伺服器資源關聯至建立的 DCR,讓記錄流向 Log Analytics 工作區。 有選項可將伺服器關聯至 DCR。

使用 Azure 入口網站或 CLI 將所選已啟用 Arc 的伺服器關聯至 DCR

在 Azure 入口網站中,使用 [資源] 區段將已啟用 Arc 的伺服器資源新增至 DCR。

使用此連結,以取得透過 Azure CLI 關聯資源的相關資訊。

使用 Azure 原則來管理 DCR 關聯

將原則指派給資源群組,以強制執行關聯。 有內建原則定義,可將 Linux Arc 機器關聯至 DCR。 使用 DCR 作為參數,將原則指派給資源群組。 這會確保資源群組內所有已啟用 Arc 的伺服器都關聯至相同的 DCR。

在 Azure 入口網站中,從原則定義頁面選取 Assign 按鈕。

為了方便起見,提供的 assign.sh 指令碼會將內建原則指派給指定資源群組,以及使用 dcr.sh 指令碼建立的 DCR。

  1. 請確定有適當的環境設定和角色必要條件,讓服務主體執行原則和角色指派。
  2. 使用 dcr.sh 指令碼在資源群組中建立 DCR,如新增資料收集規則一節中所述。
  3. 執行 assign.sh 指令碼。 這會建立原則指派和必要的角色指派。
./assign.sh

連線已啟用 Arc 的伺服器,並安裝 Azure 監視代理程式

使用內含 install.sh 指令碼,以 Arc 註冊代表 Nexus Kubernetes 叢集節點的所有伺服器 VM。 此指令碼會在 Nexus Kubernetes 叢集上建立 Kubernetes DaemonSet。 其會將 Pod 部署到每個叢集節點,將每個 VM 連線到已啟用 Arc 的伺服器,並安裝 Azure 監視代理程式 (AMA)。 daemonSet 也包含可監視伺服器連線和 AMA 程序的活躍度探查。

  1. 依照環境設定中指定的方式設定環境。 設定 Nexus Kubernetes 叢集 VM 目前的 kubeconfig 內容。
  2. 允許 Kubectl 存取 Nexus Kubernetes 叢集。

    注意

    在建立連接點 Kubernetes 叢集時,連接點會自動建立專用於儲存叢集資源的受控資源群組,並在此群組內建立 Arc 連線的叢集資源。

    若要存取叢集,您必須設定叢集連線 kubeconfig。 在使用相關的 Microsoft Entra 實體登入 Azure CLI 後,便可取得從任何位置 (甚至在其周圍的防火牆之外) 與叢集通訊所需的 kubeconfig
    1. 設定 CLUSTER_NAMERESOURCE_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. 下列命令會啟動 connectedk8s Proxy,以讓您連線到指定 Nexus Kubernetes 叢集的 Kubernetes API 伺服器。

      az connectedk8s proxy -n $CLUSTER_NAME  -g $MANAGED_RESOURCE_GROUP &
      
    4. 使用 kubectl 將要求傳送至叢集:

      kubectl get pods -A
      

      您現在應該會看到叢集的回應,其中包含所有節點的清單。

    注意

    如果您看到錯誤訊息「無法將存取權杖張貼至用戶端 proxyFailed 以連線到 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>

完成時,系統會記錄訊息:「伺服器監視已成功設定」。 屆時,已啟用 Arc 的伺服器會顯示為已選取資源群組內的資源。

注意

將這些連線的伺服器關聯至 DCR。 設定原則之後,觀察 Azure Log Analytics 工作區中的記錄可能會有一些延遲

監視 Nexus Kubernetes 叢集 – K8s 層

必要條件 - Kubernetes

運算子應該確定某些必要條件,才能在 Nexus Kubernetes 叢集上設定監視工具。

容器深入解析會將其資料儲存在 Log Analytics 工作區中。 記錄資料會流入您在「新增資料收集規則 (DCR)」一節所涵蓋初始指令碼期間提供資源識別碼的工作區。 否則,資料會流入與您的訂用帳戶相關聯資源群組中的預設工作區 (根據 Azure 位置)。

美國東部的範例如下所示:

  • Log Analytics 工作區名稱:DefaultWorkspace-<GUID>-EUS
  • 資源群組名稱:DefaultResourceGroup-EUS

執行下列命令以取得預先存在的 Log Analytics 工作區資源識別碼

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

若要在適用的 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 叢集上部署容器深入解析延伸模組:

使用客戶預先建立的 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 conectedClusters

尋找延伸模組的「成功」佈建狀態。 "k8s-extension create" 命令也可能會傳回狀態。

自訂記錄和計量收集

容器深入解析提供終端使用者功能,以微調 Nexus Kubernetes 叢集的記錄和計量收集,設定容器深入解析代理程式資料收集

額外資源