Share via


監視 Nexus Kubernetes 叢集

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

  • 虛擬機器 (VM)
  • 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 叢集與監視工具

使用受控識別驗證透過CLI上線的擴充功能

從 Azure CLI 開始的檔、如何在多個作業系統上安裝它,以及如何安裝 CLI 擴充功能

安裝最新版 的必要 CLI 擴充功能

監視 Nexus Kubernetes 叢集 – VM 層

本操作指南提供步驟和公用程式腳本,讓ArcNexus Kubernetes 叢集 虛擬機器 連線至 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

Prerequisites-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 連線 ed Machine Resource 管理員 istrator參與者 在資源群組中 連線 已啟用 Arc 的 Nexus Kubernetes 叢集 VM 伺服器,並安裝 Azure 監視代理程式 (AMA)
監視參與者參與者 在資源群組中建立 數據收集規則 (DCR), 並將已啟用 Arc 的伺服器與其產生關聯
使用者存取 管理員 istrator,以及資源原則參與者或參與者 如果您想要使用 Azure 原則指派,以確保 DCR 與 已啟用 Arc 的機器相關聯,則需要
Kubernetes 擴充功能參與者 部署 Container Insights 的 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 腳本。 它會在指定資源群組中建立名為 ${RESOURCE_GROUP}-syslog-dcr 的 DCR
./dcr.sh

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

注意

手動或透過原則建立 DCR 之前建立的伺服器建立關聯。 請參閱 補救工作

將已啟用 Arc 的伺服器資源與 DCR 產生關聯

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

使用 Azure 入口網站 或 CLI 將選取的已啟用 Arc 的伺服器與 DCR 產生關聯

在 Azure 入口網站 中,使用已啟用 Arc 的伺服器資源區段,將已啟用 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 精靈集。 它會將 Pod 部署到每個叢集節點,將每個 VM 連線到已啟用 Arc 的伺服器,並安裝 Azure 監視代理程式 (AMA)。 daemonSet也包含監視伺服器連線和 AMA 進程的活躍度探查。

  1. 設定環境,如環境設定中所指定。 設定 Nexus Kubernetes 叢集 VM 的目前 kubeconfig 內容。
  2. 允許 Kubectl 存取 Nexus Kubernetes 叢集。

    注意

    當您建立 Nexus Kubernetes 叢集時,Nexus 會自動建立專用於儲存叢集資源的受控資源群組,在此群組中建立 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 層

Prerequisites-Kubernetes

操作員必須確定在 Nexus Kubernetes 叢集上設定監視工具的某些必要條件。

Container Insights 會將其數據儲存在 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 工作區中部署 Container Insights 並檢視數據,您必須在帳戶中指派特定的角色。 例如,「參與者」角色指派。 請參閱指派必要角色指示:

  • 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 conectedClusters

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

自訂記錄和計量集合

Container Insights 提供使用者功能,以微調來自 Nexus Kubernetes Clusters 的記錄和計量集合--設定容器深入解析代理程序數據收集

額外資源

  • 檢閱 活頁簿文件 ,然後使用操作員 Nexus 遙測範例 Operator Nexus 活頁簿。
  • 檢閱 Azure 監視器警示、如何建立 Azure 監視器警示規則,以及使用範例操作員 Nexus 警示範例操作員 Nexus 警示範例操作員 Nexus 警示範例操作員 Nexus 警示範例操作員 Nexus 警示範例。