NGINX Plus 사용 리포트 F5로 전송(네트워크 제한 환경)

중요: NGINX Pluss R33은 NGINX Instance Manager 2.18 혹은 이후 버전이 필요합니다

데이터 플레인 인스턴스가 NGINX Plus R33 버전을 사용하고 있다면, 중단 없는 트래픽 처리를 보장하기 위해 NGINX Instance Manager을 2.18 혹은 이후 버전으로 업그레이드하세요.
NGINX Plus R33 인스턴스는 F5 라이선싱 엔드포인트 혹은 NGINX Instance Manager로 사용 데이터를 전송해야 합니다. 그러지 못한다면, NGINX Plus 인스턴스는 트래픽 처리를 멈춥니다.
사용 리포트 및 정책에 대한 더 자세한 정보는 구독 라이선스 문서를 참고하세요.

인터넷 접속이 불가능한 환경에서 NGINX Plus 는 사용 데이터 리포트 를 NGINX Instance Manager로 전송합니다. NGINX Instance Manager에서 사용 리포트를 다운로드하고, 인터넷 접속이 가능한 환경에서 F5로 사용 리포트를 전송해야 합니다. F5가 리포트를 검증한 이후, 사용 승인을 다운로드 할 수 있습니다. 사용 승인은 NGINX Instance Manager로 업로드되어야 합니다.

목차

1. NGINX Plus 사용 리포트 전송을 위한 사전 준비
2. F5로 NGINX Plus 사용 리포트 전송

 2-1. Bash 스크립트
 2-2. REST
 2-3. 웹 인터페이스

3. 전송되는 내용

1. NGINX Plus 사용 리포트 전송을 위한 사전 준비

사용 데이터를 F5로 전송하기 전에, NGINX Plus가 원격 측정 데이터를 NGINX Instance Manager로 전송하도록 설정해야 합니다.

NGINX Plus(R33 혹은 이후 버전)가 NGINX Instance Manager로 사용 데이터를 전송하도록 설정하려면 다음 단계를 따르세요.

1. NGINX Instance Manager을 위해 443 포트를 개방합니다.

2. 각 NGINX Plus 인스턴스의 NGINX 설정(/etc/nginx/nginx.conf)에서 mgmt 블록의 usage_report 지시문에 NGINX Instance Manager 호스트를 설정합니다.

mgmt {
  usage_report endpoint=<NGINX-INSTANCE-MANAGER-FQDN>;
}

자체 서명 인증서

자체 서명 인증서 사용을 위한 세부 사항은 자체 서명 인증서로 사용 리포트 전송을 위한 SSL 검증 설정을 참고하세요.

3. NGINX를 reload 합니다.

$ nginx -s reload

2. F5로 NGINX Plus 사용 리포트 전송

REST API 사용

curl 혹은 Postman과 같은 도구를 사용하여 NGINX Instance Manager REST API와 상호작용을 할 수 있습니다. API URL은 https://<NIM-FQDN>/api/[nim|platform]/<API_VERSION>이며, 각 요청에는 인증이 필요합니다. 인증에 대한 세부 정보는 API 개요를 참고하세요.

2-1. Bash 스크립트(권장)

네트워크 제한 환경에서 라이선스를 추가하고 최초 리포트를 전송하려면, 제공된 license_usage_offline.sh 스크립트를 사용하세요. NGINX Instance Manager에 접근할 수 있고 443 포트로 https://product.apis.f5.com/에 연결할 수 있는 시스템에서 이 스크립트를 실행하세요. 각 예시 텍스트를 변경해서 사용하세요.

1. license_usage_offline.sh 스크립트를 다운로드합니다.

2. 다음 명령어를 사용하여 스크립트를 실행할 수 있도록 합니다.

$ chmod +x <path-to-script>/license_usage_offline.sh

3. 스크립트를 실행합니다. 각 예시 텍스트를 변경해서 사용합니다.

./license_usage_offline.sh \
  -j <license-filename>.jwt \
  -i <NIM-IP-address> \
  -u admin \
  -p <password> \
  -o report.zip \
  -s initial

위 명령어는 라이선스를 추가하고, 최초 사용 리포트(report.zip)를 다운로드하고, 사용 승인을 위해 F5로 리포트를 전송하며 사용 승인을 다시 NGINX Instance Manager로 업로드합니다.

license_usage_offline.sh 스크립트 전체 내용 보기
#!/bin/bash

# Function to encode the username and password to base64
encode_base64() {
  echo -n "$1:$2" | base64
}

# Print help text and exit.
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
  echo
  echo "Usage: $0 -j  -i  -u  -p  -o  -s "
  echo
  echo "This script allows you to license NGINX Instance Manager and submit usage reports to F5 in a disconnected environment."
  echo "It needs to be run on a system that can reach NGINX Instance Manager and the following endpoint on port 443: https://product.apis.f5.com/"
  echo
  echo "If NGINX Instance Manager is connected to the internet (Connected Mode), please use the UI."
  echo "This script is only necessary if NGINX Instance Manager is set to Disconnected Mode."
  echo
  echo "For Licensing in Disconnected Mode:"
  echo "$0 -j my-jwt.jwt -i  -u admin -p  -o report.zip -s initial"
  echo 
  echo "For Usage Reporting in Disconnected Mode:"
  echo "$0 -j my-jwt.jwt -i  -u admin -p  -o report.zip -s telemetry"
  echo 
  echo "Note: Since NGINX Instance Manager comes with self-signed certificates by default, the --insecure flag is set in this script to run specific Curl commands." 
  echo "You can alter this script if you wish to change from self-signed to verified certificates in Instance Manager."
  echo 
  exit 1
fi

if ! command -v jq &> /dev/null; then
	echo -e "\nPlease install jq (https://jqlang.github.io/jq/) as it is required to run the script\n"
	exit 1
fi

# Parse command-line arguments
while getopts ":j:i:u:p:o:s:" opt; do
  case $opt in
    j) jwt_file="$OPTARG" ;;
    i) ip_address="$OPTARG" ;;
    u) username="$OPTARG" ;;
    p) password="$OPTARG" ;;
    o) output_file="$OPTARG" ;;
    s) step="$OPTARG" ;;
    \?) echo "Invalid option -$OPTARG" >&2 ;;
  esac
done


# Check if JWT_FILE is not empty and if the file exists
if [ -z "$jwt_file" ]; then
    echo "JWT file path is not defined. Please set the JWT_FILE variable."
    exit 1
elif [ ! -f "$jwt_file" ]; then
    echo "JWT file not found: $jwt_file"
    exit 1
else
    echo "JWT file found: $jwt_file"
fi


# Read the Bearer token from the file
bearer_token=$(<"$jwt_file")

# Encode the username and password to base64 for Basic Authorization
basic_auth=$(encode_base64 "$username" "$password")


# Initialize report_save_path if it's empty
report_save_path=${output_file:-"report.zip"}

############################################################################
# Step 1: Upload JWT to NGINX Instance Manager and Download telemetry report
############################################################################
echo "###################################################################"
echo "# (if not already licensed) Upload JWT and Download the Telemetry report from NGINX Instance Manager  #"
echo "###################################################################"
if [ "$step" == "initial" ]; then

    # Function to print banner
    print_banner() {
        echo "===================================="
        echo "$1"
        echo "===================================="
    }

    # Step 1: POST request
    print_banner "Executing Step 1: POST request"
    post_response=$(curl -k --location "https://$ip_address/api/platform/v1/license?telemetry=true" \
        --header "Origin: https://$ip_address" \
        --header "Referer: https://$ip_address/ui/settings/license" \
        --header "Content-Type: application/json" \
        --header "Authorization: Basic $basic_auth" \
        --data "{
            \"metadata\": {
                \"name\": \"license\"
            },
            \"desiredState\": {
                \"content\": \"$bearer_token\"
            }
        }")
    # Use jq to extract modeOfOperation
    modeOfOperation=$(jq -r '.currentStatus.modeOfOperation' <<< "$post_response")

    # Output the modeOfOperation
    echo "Mode of Operation: $modeOfOperation"

    # Parse the modeOfOperation from the response
    echo $modeOfOperation
    # Step 2: Polling for the license status with conditional logic based on modeOfOperation
    while true; do
        print_banner "Checking License Status"

        response=$(curl -k "https://$ip_address/api/platform/v1/license" \
            -H "accept: application/json" \
            -H "authorization: Basic $basic_auth" \
            -H "referer: https://$ip_address/ui/settings/license" \
            --insecure)
        fileType=$(jq -r '.currentStatus.state.currentInstance.fileType' <<< "$response")
        status=$(jq -r '.currentStatus.state.currentInstance.status' <<< "$response")
        telemetry=$(jq -r '.currentStatus.state.currentInstance.telemetry' <<< "$response")

        echo $fileType
        echo $status
        echo $telemetry
        if [[ "$modeOfOperation" == "CONNECTED" ]]; then
        if [[ "$fileType" == "JWT" && "$status" == "ACTIVATED" && "$telemetry" == "true" ]]; then
                echo "Connected mode: Desired response received!"
                break
            else
                echo "Connected mode: Waiting for the desired response..."
            fi
        elif [[ "$modeOfOperation" == "DISCONNECTED" ]]; then
            if [[ "$fileType" == "JWT" && "$status" == "INITIALIZE_ACTIVATION_COMPLETE" ]]; then
                echo "Disconnected mode: Desired response received!"
                break
            else
                echo "Disconnected mode: Waiting for the desired response..."
            fi
        fi

        sleep 5  # Poll every 5 seconds
    done

    # Step 3: PUT request
    print_banner "Executing Step 2: PUT request"
    put_response=$(curl -k --location --request PUT "https://$ip_address/api/platform/v1/license?telemetry=true" \
        --header "Origin: https://$ip_address" \
        --header "Referer: https://$ip_address/ui/settings/license" \
        --header "Content-Type: application/json" \
        --header "Authorization: Basic $basic_auth" \
        --data '{
            "desiredState": {
                "content": "",
                "type": "JWT",
                "features": [
                    {"limit": 0, "name": "NGINX_NAP_DOS", "valueType": ""},
                    {"limit": 0, "name": "IM_INSTANCES", "valueType": ""},
                    {"limit": 0, "name": "TM_INSTANCES", "valueType": ""},
                    {"limit": 0, "name": "DATA_PER_HOUR_GB", "valueType": ""},
                    {"limit": 0, "name": "NGINX_INSTANCES", "valueType": ""},
                    {"limit": 0, "name": "NGINX_NAP", "valueType": ""},
                    {"limit": 0, "name": "SUCCESSFUL_API_CALLS_MILLIONS", "valueType": ""},
                    {"limit": 0, "name": "IC_PODS", "valueType": ""},
                    {"limit": 0, "name": "IC_K8S_NODES", "valueType": ""}
                ]
            },
            "metadata": {
                "name": "license"
            }
        }')

    echo "Response from Step 2: PUT request:"

    # Step 4: Polling for the desired license status
    while true; do
        print_banner "Checking License Validation Status"

        response=$(curl -s "https://$ip_address/api/platform/v1/license" \
            -H "accept: application/json" \
            -H "authorization: Basic $basic_auth" \
            -H "referer: https://$ip_address/ui/settings/license" \
            --insecure)
        fileType=$(jq -r '.currentStatus.state.currentInstance.fileType' <<< "$response")
        status=$(jq -r '.currentStatus.state.currentInstance.status' <<< "$response")
        telemetry=$(jq -r '.currentStatus.state.currentInstance.telemetry' <<< "$response")


        if [[ "$modeOfOperation" == "CONNECTED" ]]; then
            if [[ "$fileType" == "JWT" && "$status" == "VALID" ]]; then
                echo "Connected mode: Desired response received!"
                break
            else
                echo "Connected mode: Current license response does not meet conditions. Retrying..."
            fi
        elif [[ "$modeOfOperation" == "DISCONNECTED" ]]; then
            if [[ "$fileType" == "JWT" && "$status" == "CONFIG_REPORT_READY" ]]; then
                echo "Disconnected mode: Desired response received!"
                break
            else
                echo "Disconnected mode: Current license response does not meet conditions. Retrying..."
            fi
        fi

        sleep 5
    done

    # Step 5: Loop to check if licensed
    while true; do
        print_banner "Checking Licensed Status"

        licensed_response=$(curl -s "https://$ip_address/api/platform/v1/modules/licensed" \
            -H "accept: application/json" \
            -H "authorization: Basic $basic_auth" \
            -H "referer: https://$ip_address/ui/settings/license" \
            --insecure)

        licensed_status=$(jq -r '.licensed' <<< "$licensed_response" )
        if [[ "$modeOfOperation" == "CONNECTED" ]]; then

            if [[ "$licensed_status" == "true" ]]; then
                echo "The system is licensed on connected mode!"
                break
            else
                echo "The system is not licensed yet. Retrying..."
            fi
        elif [[ "$modeOfOperation" == "DISCONNECTED" ]]; then
            if [[ "$licensed_status" == "false" ]]; then
                echo "Upload JWT success on disconnected mode"
                break
        fi
        fi

        sleep 5
    done

    echo "Requests executed successfully."

#################################################
# Step 2: Download the telemetry report from NGINX Instance Manager #
#################################################
echo "#################################################"
echo "# Download the telemetry report from NGINX Instance Manager #"
echo "#################################################"

  echo "Executing for step: initial"
  download_usage_command="curl --insecure --location 'https://$ip_address/api/platform/v1/report/download?format=zip&reportType=initial' \
          --header 'accept: */*' \
          --header 'authorization: Basic $basic_auth' \
          --output \"$report_save_path\""

elif [ "$step" == "telemetry" ]; then
  echo "Executing for step: telemetry"
  prepare_usage_command="curl --insecure --location 'https://$ip_address/api/platform/v1/report/download?format=zip&reportType=telemetry&telemetryAction=prepare' \
          --header 'accept: application/json' \
          --header 'authorization: Basic $basic_auth' \
          --header 'referer: https://$ip_address/ui/settings/license'"

  download_usage_command="curl --insecure --location 'https://$ip_address/api/platform/v1/report/download?format=zip&reportType=telemetry&telemetryAction=download' \
  --header 'accept: */*' \
  --header 'authorization: Basic $basic_auth' \
  --output \"$report_save_path\""
fi
if [ "$step" == "telemetry" ]; then
  echo "Running telemetry stage: "

  # Run the saved command and store the response
  response=$(eval $prepare_usage_command)

  # Print the response
  echo "Response: $response"
  sleep 2
  # Validate if the response contains "Report generation in progress"
  if echo "$response" | grep -q '"telemetry":"Report generation in progress"'; then
    echo "Success: Report generation is in progress."
  else
    echo "Failure: Report generation not in progress or unexpected response."
    exit 1
  fi

    echo "Running command: $download_usage_command"
    eval $download_usage_command
  else
    echo "Running command: $download_usage_command"
    eval $download_usage_command
  fi

############################################################
# Step 3: Upload the telemetry report to F5's licensing endpoint
############################################################
echo "############################################################"
echo "# Upload the telemetry report to F5's licensing endpoint  #"
echo "############################################################"

echo "Uploading $report_save_path to telemetry endpoint..."
upload_command="curl --location 'https://product.apis.f5.com/ee/v1/entitlements/telemetry/bulk' \
--header 'Authorization: Bearer $bearer_token' \
--form 'file=@\"$report_save_path\"'"

response=$(eval $upload_command)
echo "Response from upload: $response"

############################################################
# Step 4: Extract the status ID from the response
############################################################
echo "############################################################"
echo "# Extract status ID from the response#"
echo "############################################################"

status_link=$(jq -r '.statusLink | split("/") | last' <<< "$response")
if [ -z "$status_link" ]; then
  echo "Failed to extract status link from response. Please try again."
  exit 1
fi
echo "Extracted status link ID: $status_link"

##############################################################
# Step 5: Check the status to ensure the acknowledgement file is ready to download
############################################################
echo "############################################################"
echo "# Check the status to ensure the acknowledgement file is ready to download     #"
echo "############################################################"

# Initialize variables for the loop
max_attempts=10
attempt=0

# Loop to check the status until percentageComplete and percentageSuccessful are 100 or the max attempts is reached
while [ $attempt -lt $max_attempts ]; do
  echo "Checking status for the upload... (Attempt: $((attempt + 1)))"

  # Run the curl command to get the status
  status_command="curl --location 'https://product.apis.f5.com/ee/v1/entitlements/telemetry/bulk/status/$status_link' --header 'Authorization: Bearer $bearer_token'"

  status_response=$(eval $status_command)

  # Extract values from the response
  percentage_complete=$(jq -r '.percentageComplete' <<< "$status_response")
  percentage_successful=$(jq -r '.percentageSuccessful' <<<  "$status_response")
  ready_for_download=$(jq -r '.readyForDownload' <<< "$status_response")

  echo "Percentage Complete: $percentage_complete"
  echo "Percentage Successful: $percentage_successful"
  echo "Ready for Download: $ready_for_download"

  # Check if the report is ready for download
  if [ "$percentage_complete" == "100" ] && [ "$percentage_successful" == "100" ] && [ "$ready_for_download" == "true" ]; then
    echo "File is ready for download."
    break
  else
    echo "File is not ready for download yet. Sleeping for 2 seconds..."
    sleep 2
  fi

  # Increment the attempt counter
  attempt=$((attempt + 1))
done

# If after 10 attempts it's still not ready, show a message
if [ $attempt -eq $max_attempts ]; then
  echo "Reached maximum attempts. The file is not ready for download."
  exit 1
fi

############################################################
# Step 6: Download the acknowledgement file from F5's licensing endpoint
############################################################
echo "############################################################"
echo "# Download the acknowledgement report from F5's licensing endpoint #"
echo "############################################################"

# Extract downloadLink
download_link=$(jq -r '.downloadLink | split("/") | last' <<< "$status_response")
echo $download_link
# Ensure the report_save_path is not empty
if [ -z "$report_save_path" ]; then
  # If the report_save_path is empty, set a default path and filename
  report_save_path="report_download.zip"
fi

echo "Downloading file from telemetry..."

download_command="curl --location 'https://product.apis.f5.com/ee/v1/entitlements/telemetry/bulk/download/$download_link' \
        --header 'Authorization: Bearer $bearer_token' \
        --output '$report_save_path'"

curl --location "https://product.apis.f5.com/ee/v1/entitlements/telemetry/bulk/download/$download_link" \
--header "Authorization: Bearer $bearer_token" \
--output "$report_save_path"

echo "Downloaded file saved as: $report_save_path"

############################################################
# Step 7: Upload the acknowledgement report to NGINX Instance Manager
############################################################
echo "############################################################"
echo "# Upload the acknowledgement report to NGINX Instance Manager   #"
echo "############################################################"

curl --insecure --location "https://$ip_address/api/platform/v1/report/upload" \
--header "Authorization: Basic $basic_auth" \
--form "file=@\"$report_save_path\"" \
--silent --output /dev/null

echo "Report acknowledgement successfully uploaded to NGINX Instance Manager $ip_address."

2-2. REST

curl을 사용해 사용 리포트를 전송하기 위해 다음 단계를 따르세요.

NGINX Instance Manager에 접근할 수 있고 443 포트로 https://product.apis.f5.com/에 연결할 수 있는 시스템에서 다음 curl 명령어를 사용하세요. 각 예시 텍스트를 환경에 맞게 변경해서 사용하세요.

중요:

-k 플래그는 SSL 인증서 검증을 건너뜁니다. NGINX Instance Manager가 자체 서명 인증서를 사용하거나, 시스템이 인증서를 신뢰하지 않을 경우에만 사용하세요.

1. 사용 리포트를 준비합니다.

$ curl -k --location 'https://<NIM-FQDN>/api/platform/v1/report/download?format=zip&reportType=telemetry&telemetryAction=prepare' \
--header 'accept: application/json' \
--header 'authorization: Basic <base64-encoded-credentials>' \
--header 'referer: https://<NIM-FQDN>/ui/settings/license'

2. 사용 리포트를 다운로드합니다.

$ curl -k --location 'https://<NIM-FQDN>/api/platform/v1/report/download?format=zip&reportType=telemetry&telemetryAction=download' \
--header 'accept: */*' \
--header 'authorization: Basic <base64-encoded-credentials>' \
--output report.zip

3. 확인을 위해 F5에 사용 리포트를 전송합니다.

$ curl --location 'https://product.apis.f5.com/ee/v1/entitlements/telemetry/bulk' \
--header "Authorization: Bearer $(cat /path/to/jwt-file)" \
--form 'file=@"<path-to-report>.zip"'

명령어를 실행한 이후, 응답의 “statusLink”를 확인하세요. “statusLink” 값(the UUID)의 마지막 부분이 report-id입니다. 예시는 다음과 같습니다.

{"statusLink":"/status/2214e480-3401-43a3-a54c-9dc501a01f83"}

예시의 경우, report-id 는 2214e480-3401-43a3-a54c-9dc501a01f83 입니다.
다음 단계에서 report-id가 필요합니다.

4. 사용 승인의 상태를 확인합니다.
report-id를 이전 응답의 값으로 변경하세요.

$ curl --location 'https://product.apis.f5.com/ee/v1/entitlements/telemetry/bulk/status/<report-id>' \
--header "Authorization: Bearer $(cat /path/to/jwt-file)"

5. F5에서 사용 승인을 다운로드합니다.

$ curl --location 'https://product.apis.f5.com/ee/v1/entitlements/telemetry/bulk/download/<report-id>' \
--header "Authorization: Bearer $(cat /path/to/jwt-file)" \
--output <path-to-acknowledgement>.zip

6. 사용 승인을 NGINX Instance Manager로 업로드합니다.

$ curl -k --location 'https://<NIM-FQDN>/api/platform/v1/report/upload' \
--header 'Authorization: Basic <base64-encoded-credentials>' \
--form 'file=@"<path-to-acknowledgement>.zip"'

2-3. 웹 인터페이스

1. 사용 리포트 다운로드

F5로 전송하기 위해 최초 사용 리포트를 다운로드합니다.

  • Licenses > Overview 페이지에서 Download License Report를 선택합니다.

2. F5로 사용 리포트 전송

REST 방식을 통해 사용 리포트를 F5로 전송하고 사용 승인을 다운받아야 합니다. REST 섹션의 3-5단계를 따르세요.

3. NGINX Instance Manager로 사용 승인 업로드

  1. Licenses > Overview 페이지에서 Upload Usage Acknowledgement를 선택합니다.
  2. Browse를 선택하거나 파일을 드래그하여 사용 승인을 업로드합니다.
  3. Add를 선택합니다.

3. 전송되는 내용

NGINX Plus는 사용 리포트를 시간마다 자동으로 F5에 전송합니다. 이 데이터는 POST 요청으로 전송되고 처리한 트래픽 양이나 인스턴스가 실행된 기간과 같은 세부 정보를 포함합니다. 다음은 전송되는 데이터의 예시입니다.

{
"version": "<nginx_version>",
"uuid": "<nginx_uuid>",
"nap": "<active/inactive>", // status of NGINX App Protect
"http": {
"client": {
"received": 0, // bytes received
"sent": 0, // bytes sent
"requests": 0 // number of HTTP requests processed
},
"upstream": {
"received": 0, // bytes received
"sent": 0 // bytes sent
}
},
"stream": {
"client": {
"received": 0, // bytes received
"sent": 0 // bytes sent
},
"upstream": {
"received": 0, // bytes received
"sent": 0 // bytes sent
}
},
"workers": 0, // number of worker processes running
"uptime": 0, // number of seconds the instance has been running
"reloads": 0, // number of times the instance has been reloaded
"start_time": "epoch", // start time of data collection for the report
"end_time": "epoch" // end time of data collection for the report
}

NGINX STORE를 통한 솔루션 도입 및 기술지원 무료 상담 신청

* indicates required