diff --git a/.gitignore b/.gitignore index 791b4ebf6..957a66fa5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ _themes*/ _repo.*/ .openpublishing.buildcore.ps1 -ie.log \ No newline at end of file +ie.log +report.json \ No newline at end of file diff --git a/azure-vote-nginx-ssl.yml b/azure-vote-nginx-ssl.yml deleted file mode 100644 index d03fd94b1..000000000 --- a/azure-vote-nginx-ssl.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- -# INGRESS WITH SSL PROD -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: vote-ingress - namespace: default - annotations: - kubernetes.io/tls-acme: "true" - nginx.ingress.kubernetes.io/ssl-redirect: "true" - cert-manager.io/cluster-issuer: letsencrypt-prod -spec: - ingressClassName: nginx - tls: - - hosts: - - mydnslabel9730fc.westeurope.cloudapp.azure.com - secretName: azure-vote-nginx-secret - rules: - - host: mydnslabel9730fc.westeurope.cloudapp.azure.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: azure-vote-front - port: - number: 80 diff --git a/cloud-init.txt b/cloud-init.txt deleted file mode 100644 index 12bd08305..000000000 --- a/cloud-init.txt +++ /dev/null @@ -1,105 +0,0 @@ -#cloud-config -# Install, update, and upgrade packages -package_upgrade: true -package_update: true -package_reboot_if_require: true -# Install packages -packages: - - vim - - certbot - - python3-certbot-nginx - - bash-completion - - nginx - - mysql-client - - php - - php-cli - - php-bcmath - - php-curl - - php-imagick - - php-intl - - php-json - - php-mbstring - - php-mysql - - php-gd - - php-xml - - php-xmlrpc - - php-zip - - php-fpm -write_files: - - owner: www-data:www-data - path: /etc/nginx/sites-available/default.conf - content: | - server { - listen 80 default_server; - listen [::]:80 default_server; - root /var/www/html; - server_name mydnslabel28fb9f.westeurope.cloudapp.azure.com; - } -write_files: - - owner: www-data:www-data - path: /etc/nginx/sites-available/mydnslabel28fb9f.westeurope.cloudapp.azure.com.conf - content: | - upstream php { - server unix:/run/php/php8.1-fpm.sock; - } - server { - listen 443 ssl http2; - listen [::]:443 ssl http2; - server_name mydnslabel28fb9f.westeurope.cloudapp.azure.com; - ssl_certificate /etc/letsencrypt/live/mydnslabel28fb9f.westeurope.cloudapp.azure.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/mydnslabel28fb9f.westeurope.cloudapp.azure.com/privkey.pem; - root /var/www/mydnslabel28fb9f.westeurope.cloudapp.azure.com; - index index.php; - location / { - try_files $uri $uri/ /index.php?$args; - } - location ~ \.php$ { - include fastcgi_params; - fastcgi_intercept_errors on; - fastcgi_pass php; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - } - location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { - expires max; - log_not_found off; - } - location = /favicon.ico { - log_not_found off; - access_log off; - } - location = /robots.txt { - allow all; - log_not_found off; - access_log off; - } - } - server { - listen 80; - listen [::]:80; - server_name mydnslabel28fb9f.westeurope.cloudapp.azure.com; - return 301 https://mydnslabel28fb9f.westeurope.cloudapp.azure.com$request_uri; - } -runcmd: - - sed -i 's/;cgi.fix_pathinfo.*/cgi.fix_pathinfo = 1/' /etc/php/8.1/fpm/php.ini - - sed -i 's/^max_execution_time \= .*/max_execution_time \= 300/g' /etc/php/8.1/fpm/php.ini - - sed -i 's/^upload_max_filesize \= .*/upload_max_filesize \= 64M/g' /etc/php/8.1/fpm/php.ini - - sed -i 's/^post_max_size \= .*/post_max_size \= 64M/g' /etc/php/8.1/fpm/php.ini - - systemctl restart php8.1-fpm - - systemctl restart nginx - - certbot --nginx certonly --non-interactive --agree-tos -d mydnslabel28fb9f.westeurope.cloudapp.azure.com -m dummy@dummy.com --redirect - - ln -s /etc/nginx/sites-available/mydnslabel28fb9f.westeurope.cloudapp.azure.com.conf /etc/nginx/sites-enabled/ - - rm /etc/nginx/sites-enabled/default - - systemctl restart nginx - - curl --url https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar --output /tmp/wp-cli.phar - - mv /tmp/wp-cli.phar /usr/local/bin/wp - - chmod +x /usr/local/bin/wp - - wp cli update - - mkdir -m 0755 -p /var/www/mydnslabel28fb9f.westeurope.cloudapp.azure.com - - chown -R azureadmin:www-data /var/www/mydnslabel28fb9f.westeurope.cloudapp.azure.com - - sudo -u azureadmin -i -- wp core download --path=/var/www/mydnslabel28fb9f.westeurope.cloudapp.azure.com - - sudo -u azureadmin -i -- wp config create --dbhost=mydb28fb9f.mysql.database.azure.com --dbname=wp001 --dbuser=dbadmin28fb9f --dbpass="OKISjTu6H7xixUjYxP3+521zeGuH75YxtTriR87fq28=" --path=/var/www/mydnslabel28fb9f.westeurope.cloudapp.azure.com - - sudo -u azureadmin -i -- wp core install --url=mydnslabel28fb9f.westeurope.cloudapp.azure.com --title="Azure hosted blog" --admin_user=wpcliadmin --admin_password="j19pzsPcHrLBBCTzAuAHtyYgWFuy1+6odxXO7HCFzWI=" --admin_email=6ab2c105-cbe9-4ecf-971b-20034854fbca --path=/var/www/mydnslabel28fb9f.westeurope.cloudapp.azure.com - - sudo -u azureadmin -i -- wp plugin update --all --path=/var/www/mydnslabel28fb9f.westeurope.cloudapp.azure.com - - chmod 600 /var/www/mydnslabel28fb9f.westeurope.cloudapp.azure.com/wp-config.php - - mkdir -p -m 0775 /var/www/mydnslabel28fb9f.westeurope.cloudapp.azure.com/wp-content/uploads - - chgrp www-data /var/www/mydnslabel28fb9f.westeurope.cloudapp.azure.com/wp-content/uploads diff --git a/cluster-issuer-prod.yml b/cluster-issuer-prod.yml deleted file mode 100644 index e49a9a8c9..000000000 --- a/cluster-issuer-prod.yml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: cert-manager.io/v1 -kind: ClusterIssuer -metadata: - name: letsencrypt-prod -spec: - acme: - # You must replace this email address with your own. - # Let's Encrypt will use this to contact you about expiring - # certificates, and issues related to your account. - email: namanparikh@microsoft.com - # ACME server URL for Let’s Encrypt’s prod environment. - # The staging environment will not issue trusted certificates but is - # used to ensure that the verification process is working properly - # before moving to production - server: https://acme-v02.api.letsencrypt.org/directory - # Secret resource used to store the account's private key. - privateKeySecretRef: - name: letsencrypt - # Enable the HTTP-01 challenge provider - # you prove ownership of a domain by ensuring that a particular - # file is present at the domain - solvers: - - http01: - ingress: - class: nginx - podTemplate: - spec: - nodeSelector: - "kubernetes.io/os": linux diff --git a/report.json b/report.json deleted file mode 100644 index 69001f6f5..000000000 --- a/report.json +++ /dev/null @@ -1,356 +0,0 @@ -{ - "name": "Quickstart: Deploy Inspektor Gadget in an Azure Kubernetes Service cluster", - "properties": { - "author": "josebl", - "description": "This tutorial shows how to deploy Inspektor Gadget in an AKS cluster", - "ms.author": "josebl", - "ms.custom": "innovation-engine", - "ms.date": "12/06/2023", - "ms.topic": "article", - "title": "Deploy Inspektor Gadget in an Azure Kubernetes Service cluster" - }, - "environmentVariables": { - "AKS_CLUSTER_NAME": "aks-cnpg-3cee3l", - "AKS_CLUSTER_VERSION": "1.29", - "AKS_MANAGED_IDENTITY_NAME": "mi-aks-cnpg-3cee3l", - "AKS_NODE_COUNT": "2", - "AKS_PRIMARY_CLUSTER_FED_CREDENTIAL_NAME": "pg-primary-fedcred1-cnpg-l1tsugyd", - "AKS_PRIMARY_CLUSTER_NAME": "aks-primary-cnpg-l1tsugyd", - "AKS_PRIMARY_CLUSTER_PG_DNSPREFIX": "a33a3d08c14", - "AKS_PRIMARY_MANAGED_RG_NAME": "rg-cnpg-primary-aksmanaged-l1tsugyd", - "AKS_UAMI_CLUSTER_IDENTITY_NAME": "mi-aks-cnpg-l1tsugyd", - "BARMAN_CONTAINER_NAME": "barman", - "CLUSTER_VERSION": "1.27", - "ENABLE_AZURE_PVC_UPDATES": "true", - "ERROR": "\u001b[31m", - "IP_ADDRESS": "52.233.203.69", - "KEYVAULT_NAME": "kv-cnpg-3cee3l", - "LOCAL_NAME": "cnpg", - "LOCATION": "eastus", - "MOTD_SHOWN": "update-motd", - "MY_AKS_CLUSTER_NAME": "myAKSClusterb60d78", - "MY_COMPUTER_VISION_NAME": "computervisiont6xygvc3", - "MY_CONTAINER_APP_ENV_NAME": "containerappenvt6xygvc3", - "MY_CONTAINER_APP_NAME": "containerappt6xygvc3", - "MY_DATABASE_NAME": "dbt6xygvc3", - "MY_DATABASE_PASSWORD": "dbpasst6xygvc3", - "MY_DATABASE_SERVER_NAME": "dbservert6xygvc3", - "MY_DATABASE_USERNAME": "dbusert6xygvc3", - "MY_DNS_LABEL": "mydnslabel3f8d9e", - "MY_RESOURCE_GROUP_NAME": "myResourceGroupb60d78", - "MY_STATIC_WEB_APP_NAME": "myStaticWebApp85f4f3", - "MY_STORAGE_ACCOUNT_NAME": "storaget6xygvc3", - "MY_USERNAME": "azureuser", - "MY_VM_IMAGE": "Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts-gen2:latest", - "MY_VM_NAME": "myVMecb9fc", - "MyAction": "allow", - "MyAddressPrefix": "0.0.0.0/0", - "MyAddressPrefixes1": "10.0.0.0/8", - "MyAddressPrefixes2": "10.10.1.0/24", - "MyAddressPrefixes3": "10.20.1.0/24", - "MyAddressPrefixes4": "10.100.1.0/26", - "MyAddressPrefixes5": "10.30.1.0/24", - "MyAdminUsername": "d95734", - "MyApiserverVisibility": "Private", - "MyCollectionName1": "AROd95734", - "MyCollectionName2": "Dockerd95734", - "MyCustomData": "cloud_init_upgrade.txt", - "MyDearmor": "-o", - "MyDisablePrivateLinkServiceNetworkPolicies": "true", - "MyGenerateSshKeys": "export", - "MyImage": "Ubuntu2204", - "MyIngressVisibility": "Private", - "MyMasterSubnet": "-master", - "MyName": "NetworkWatcherAgentLinux2ef723", - "MyName1": "ubuntu-jumpd95734", - "MyName2": "aro-udrd95734", - "MyName3": "-masterd95734", - "MyName4": "-workerd95734", - "MyNextHopType": "VirtualAppliance", - "MyPriority1": "100", - "MyPriority2": "200", - "MyProtocols": "http=80", - "MyPublicIpAddress1": "jumphost-ip", - "MyPublicIpAddress2": "fw-ip", - "MyPublisher": "Microsoft.Azure.NetworkWatcher", - "MyPullSecret": "@pull-secret.txt", - "MyQuery1": "ipAddress", - "MyQuery2": "ipConfigurations[0].privateIPAddress", - "MyRemove": "routeTable", - "MyResourceGroup": "d95734", - "MyRouteTable": "aro-udr", - "MyRouteTableName": "aro-udrd95734", - "MyServiceEndpoints": "Microsoft.ContainerRegistry", - "MySku": "Standard", - "MySourceAddresses": "*", - "MyTargetFqdns1": "cert-api.access.redhat.com", - "MyTargetFqdns2": "*cloudflare.docker.com", - "MyVersion": "1.4", - "MyVmName": "myVM12ef723", - "MyVnetName": "d95734", - "MyWorkerSubnet": "-worker", - "NC": "\u001b(B\u001b[m", - "OUTPUT": "\u001b[32m", - "PG_NAMESPACE": "cnpg-database", - "PG_PRIMARY_CLUSTER_NAME": "pg-primary-cnpg-l1tsugyd", - "PG_PRIMARY_STORAGE_ACCOUNT_NAME": "hacnpgpsal1tsugyd", - "PG_STORAGE_BACKUP_CONTAINER_NAME": "backups", - "PG_SYSTEM_NAMESPACE": "cnpg-system", - "PRIMARY_CLUSTER_REGION": "westus3", - "RANDOM_ID": "b60d78", - "REGION": "eastus", - "RESOURCE_GROUP_NAME": "rg-cnpg-l1tsugyd", - "RGTAGS": "owner=cnpg", - "RG_NAME": "rg-cnpg-3cee3l", - "STORAGE_ACCOUNT_NAME": "storcnpg3cee3l", - "SUFFIX": "3cee3l", - "TAGS": "owner=user" - }, - "success": false, - "error": "failed to execute code block 0 on step 2.\nError: command exited with 'exit status 1' and the message 'WARNING: The behavior of this command has been altered by the following extension: aks-preview\nERROR: (SkuNotAvailable) Preflight validation check for resource(s) for container service myAKSClusterb60d78 in resource group MC_myResourceGroupb60d78_myAKSClusterb60d78_eastus failed. Message: The requested VM size for resource 'Following SKUs have failed for Capacity Restrictions: Standard_DS2_v2' is currently not available in location 'eastus'. Please try another size or deploy to a different location or different zone. See https://aka.ms/azureskunotavailable for details.. Details: \nCode: SkuNotAvailable\nMessage: Preflight validation check for resource(s) for container service myAKSClusterb60d78 in resource group MC_myResourceGroupb60d78_myAKSClusterb60d78_eastus failed. Message: The requested VM size for resource 'Following SKUs have failed for Capacity Restrictions: Standard_DS2_v2' is currently not available in location 'eastus'. Please try another size or deploy to a different location or different zone. See https://aka.ms/azureskunotavailable for details.. Details: \n'\nStdErr: WARNING: The behavior of this command has been altered by the following extension: aks-preview\nERROR: (SkuNotAvailable) Preflight validation check for resource(s) for container service myAKSClusterb60d78 in resource group MC_myResourceGroupb60d78_myAKSClusterb60d78_eastus failed. Message: The requested VM size for resource 'Following SKUs have failed for Capacity Restrictions: Standard_DS2_v2' is currently not available in location 'eastus'. Please try another size or deploy to a different location or different zone. See https://aka.ms/azureskunotavailable for details.. Details: \nCode: SkuNotAvailable\nMessage: Preflight validation check for resource(s) for container service myAKSClusterb60d78 in resource group MC_myResourceGroupb60d78_myAKSClusterb60d78_eastus failed. Message: The requested VM size for resource 'Following SKUs have failed for Capacity Restrictions: Standard_DS2_v2' is currently not available in location 'eastus'. Please try another size or deploy to a different location or different zone. See https://aka.ms/azureskunotavailable for details.. Details: \n", - "failedAtStep": -1, - "steps": [ - { - "codeBlock": { - "language": "bash", - "content": "if ! [ -x \"$(command -v kubectl)\" ]; then az aks install-cli; fi\n", - "header": "Connect to the cluster", - "description": "Install az aks CLI locally using the az aks install-cli command", - "resultBlock": { - "language": "", - "content": "", - "expectedSimilarityScore": 0, - "expectedRegexPattern": null - } - }, - "codeBlockNumber": 0, - "error": null, - "stdErr": "", - "stdOut": "", - "stepName": "Connect to the cluster", - "stepNumber": 3, - "success": false, - "similarityScore": 0 - }, - { - "codeBlock": { - "language": "bash", - "content": "IG_VERSION=$(curl -s https://api.github.com/repos/inspektor-gadget/inspektor-gadget/releases/latest | jq -r .tag_name)\nIG_ARCH=amd64\nmkdir -p $HOME/.local/bin\nexport PATH=$PATH:$HOME/.local/bin\ncurl -sL https://github.com/inspektor-gadget/inspektor-gadget/releases/download/${IG_VERSION}/kubectl-gadget-linux-${IG_ARCH}-${IG_VERSION}.tar.gz | tar -C $HOME/.local/bin -xzf - kubectl-gadget\n", - "header": "Installing the kubectl plugin: `gadget`", - "description": "[!NOTE]\nIf you want to install it using [`krew`](https://sigs.k8s.io/krew) or compile it from the source, please follow the official documentation: [installing kubectl gadget](https://github.com/inspektor-gadget/inspektor-gadget/blob/main/docs/install.md#installing-kubectl-gadget).", - "resultBlock": { - "language": "", - "content": "", - "expectedSimilarityScore": 0, - "expectedRegexPattern": null - } - }, - "codeBlockNumber": 0, - "error": null, - "stdErr": "", - "stdOut": "", - "stepName": "Installing the kubectl plugin: `gadget`", - "stepNumber": 4, - "success": false, - "similarityScore": 0 - }, - { - "codeBlock": { - "language": "bash", - "content": "kubectl gadget version\n", - "header": "Installing Inspektor Gadget in the cluster", - "description": "Now, let’s verify the installation by running the `version` command again:", - "resultBlock": { - "language": "text", - "content": "Client version: vX.Y.Z\nServer version: vX.Y.Z\n", - "expectedSimilarityScore": 0, - "expectedRegexPattern": "(?m)^Client version: v\\d+\\.\\d+\\.\\d+$\\n^Server version: v\\d+\\.\\d+\\.\\d+$" - } - }, - "codeBlockNumber": 1, - "error": null, - "stdErr": "", - "stdOut": "", - "stepName": "Installing Inspektor Gadget in the cluster", - "stepNumber": 5, - "success": false, - "similarityScore": 0 - }, - { - "codeBlock": { - "language": "bash", - "content": "kubectl gadget help\n", - "header": "Installing Inspektor Gadget in the cluster", - "description": "You can now start running the gadgets:", - "resultBlock": { - "language": "", - "content": "", - "expectedSimilarityScore": 0, - "expectedRegexPattern": null - } - }, - "codeBlockNumber": 2, - "error": null, - "stdErr": "", - "stdOut": "", - "stepName": "Installing Inspektor Gadget in the cluster", - "stepNumber": 5, - "success": false, - "similarityScore": 0 - }, - { - "codeBlock": { - "language": "bash", - "content": "az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION\n", - "header": "Create a resource group", - "description": "A resource group is a container for related resources. All resources must be placed in a resource group. We will create one for this tutorial. The following command creates a resource group with the previously defined $MY_RESOURCE_GROUP_NAME and $REGION parameters.", - "resultBlock": { - "language": "JSON", - "content": "{\n \"id\": \"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup210\",\n \"location\": \"eastus\",\n \"managedBy\": null,\n \"name\": \"testResourceGroup\",\n \"properties\": {\n \"provisioningState\": \"Succeeded\"\n },\n \"tags\": null,\n \"type\": \"Microsoft.Resources/resourceGroups\"\n}\n", - "expectedSimilarityScore": 0.3, - "expectedRegexPattern": null - } - }, - "codeBlockNumber": 0, - "error": null, - "stdErr": "", - "stdOut": "{\n \"id\": \"/subscriptions/325e7c34-99fb-4190-aa87-1df746c67705/resourceGroups/myResourceGroupb60d78\",\n \"location\": \"eastus\",\n \"managedBy\": null,\n \"name\": \"myResourceGroupb60d78\",\n \"properties\": {\n \"provisioningState\": \"Succeeded\"\n },\n \"tags\": null,\n \"type\": \"Microsoft.Resources/resourceGroups\"\n}\n", - "stepName": "Create a resource group", - "stepNumber": 1, - "success": true, - "similarityScore": 0.7850672214487863 - }, - { - "codeBlock": { - "language": "bash", - "content": "az aks create \\\n --resource-group $MY_RESOURCE_GROUP_NAME \\\n --name $MY_AKS_CLUSTER_NAME \\\n --location $REGION \\\n --no-ssh-key\n", - "header": "Create AKS Cluster", - "description": "This will take a few minutes.", - "resultBlock": { - "language": "", - "content": "", - "expectedSimilarityScore": 0, - "expectedRegexPattern": null - } - }, - "codeBlockNumber": 0, - "error": {}, - "stdErr": "WARNING: The behavior of this command has been altered by the following extension: aks-preview\nERROR: (SkuNotAvailable) Preflight validation check for resource(s) for container service myAKSClusterb60d78 in resource group MC_myResourceGroupb60d78_myAKSClusterb60d78_eastus failed. Message: The requested VM size for resource 'Following SKUs have failed for Capacity Restrictions: Standard_DS2_v2' is currently not available in location 'eastus'. Please try another size or deploy to a different location or different zone. See https://aka.ms/azureskunotavailable for details.. Details: \nCode: SkuNotAvailable\nMessage: Preflight validation check for resource(s) for container service myAKSClusterb60d78 in resource group MC_myResourceGroupb60d78_myAKSClusterb60d78_eastus failed. Message: The requested VM size for resource 'Following SKUs have failed for Capacity Restrictions: Standard_DS2_v2' is currently not available in location 'eastus'. Please try another size or deploy to a different location or different zone. See https://aka.ms/azureskunotavailable for details.. Details: \n", - "stdOut": "", - "stepName": "Create AKS Cluster", - "stepNumber": 2, - "success": false, - "similarityScore": 0 - }, - { - "codeBlock": { - "language": "bash", - "content": "az aks get-credentials --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_AKS_CLUSTER_NAME --overwrite-existing\n", - "header": "Connect to the cluster", - "description": "[!WARNING]\nThis will overwrite any existing credentials with the same entry", - "resultBlock": { - "language": "", - "content": "", - "expectedSimilarityScore": 0, - "expectedRegexPattern": null - } - }, - "codeBlockNumber": 1, - "error": null, - "stdErr": "", - "stdOut": "", - "stepName": "Connect to the cluster", - "stepNumber": 3, - "success": false, - "similarityScore": 0 - }, - { - "codeBlock": { - "language": "bash", - "content": "kubectl get nodes\n", - "header": "Connect to the cluster", - "description": "Verify the connection to your cluster using the kubectl get command. This command returns a list of the cluster nodes.", - "resultBlock": { - "language": "", - "content": "", - "expectedSimilarityScore": 0, - "expectedRegexPattern": null - } - }, - "codeBlockNumber": 2, - "error": null, - "stdErr": "", - "stdOut": "", - "stepName": "Connect to the cluster", - "stepNumber": 3, - "success": false, - "similarityScore": 0 - }, - { - "codeBlock": { - "language": "bash", - "content": "kubectl gadget version\n", - "header": "Installing the kubectl plugin: `gadget`", - "description": "Now, let’s verify the installation by running the `version` command:", - "resultBlock": { - "language": "text", - "content": "Client version: vX.Y.Z\nServer version: not installed\n", - "expectedSimilarityScore": 0, - "expectedRegexPattern": "(?m)^Client version: v\\d+\\.\\d+\\.\\d+$\\n^Server version: not installed$" - } - }, - "codeBlockNumber": 1, - "error": null, - "stdErr": "", - "stdOut": "", - "stepName": "Installing the kubectl plugin: `gadget`", - "stepNumber": 4, - "success": false, - "similarityScore": 0 - }, - { - "codeBlock": { - "language": "bash", - "content": "kubectl gadget deploy\n", - "header": "Installing Inspektor Gadget in the cluster", - "description": "[!NOTE]\nSeveral options are available to customize the deployment: use a specific container image, deploy to specific nodes, and many others. To know all of them, please check the official documentation: [installing in the cluster](https://github.com/inspektor-gadget/inspektor-gadget/blob/main/docs/install.md#installing-in-the-cluster).", - "resultBlock": { - "language": "", - "content": "", - "expectedSimilarityScore": 0, - "expectedRegexPattern": null - } - }, - "codeBlockNumber": 0, - "error": null, - "stdErr": "", - "stdOut": "", - "stepName": "Installing Inspektor Gadget in the cluster", - "stepNumber": 5, - "success": false, - "similarityScore": 0 - }, - { - "codeBlock": { - "language": "bash", - "content": "export RANDOM_ID=\"$(openssl rand -hex 3)\"\nexport MY_RESOURCE_GROUP_NAME=\"myResourceGroup$RANDOM_ID\"\nexport REGION=\"eastus\"\nexport MY_AKS_CLUSTER_NAME=\"myAKSCluster$RANDOM_ID\"\n", - "header": "Define Environment Variables", - "description": "The First step in this tutorial is to define environment variables:", - "resultBlock": { - "language": "", - "content": "", - "expectedSimilarityScore": 0, - "expectedRegexPattern": null - } - }, - "codeBlockNumber": 0, - "error": null, - "stdErr": "", - "stdOut": "", - "stepName": "Define Environment Variables", - "stepNumber": 0, - "success": true, - "similarityScore": 1 - } - ] -} \ No newline at end of file diff --git a/scenarios/AIChatApp/ai-chat-app.md b/scenarios/AIChatApp/ai-chat-app.md new file mode 100644 index 000000000..bcd63bf38 --- /dev/null +++ b/scenarios/AIChatApp/ai-chat-app.md @@ -0,0 +1,308 @@ +--- +title: 'Tutorial: Implement RAG on Azure Cognitive Services with a Chat Interface' +description: Learn how to implement Retrieval-Augmented Generation (RAG) using Azure Cognitive Services, LangChain, ChromaDB, and Chainlit, and deploy it in Azure Container Apps. +ms.topic: tutorial +ms.date: 10/10/2023 +author: GitHubCopilot +ms.author: GitHubCopilot +ms.custom: innovation-engine +--- + +# Tutorial: Create a RAG Chat App using Azure AI Search with OpenAI in Python + +This tutorial guides you through the process of creating a Retrieval-Augmented Generation (RAG) Chat App using Azure AI Search with OpenAI in Python. + +## Prerequisites + +- An Azure account with an active subscription. +- Azure CLI installed on your local machine. +- Python 3.9 or higher installed on your local machine. +- Docker installed if you plan to containerize the application. + +## Step 1: Create Azure Resources + +1. **Set Environment Variables** + + ```bash + export RANDOM_SUFFIX=$(openssl rand -hex 3) + export RESOURCE_GROUP="myResourceGroup$RANDOM_SUFFIX" + export LOCATION="westus2" + ``` + +2. **Create a Resource Group** + + ```bash + az group create --name $RESOURCE_GROUP --location $LOCATION + ``` + + Results: + + + + ```JSON + { + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupxxx", + "location": "westus2", + "managedBy": null, + "name": "myResourceGroupxxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" + } + ``` + +3. **Create an Azure Cognitive Search Service** + + ```bash + export SEARCH_SERVICE_NAME="mySearchService$RANDOM_SUFFIX" + az search service create \ + --name $SEARCH_SERVICE_NAME \ + --resource-group $RESOURCE_GROUP \ + --location $LOCATION \ + --sku basic + ``` + + Results: + + + + ```JSON + { + "hostName": "mysearchservicexxx.search.windows.net", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupxxx/providers/Microsoft.Search/searchServices/mySearchServicexxx", + "location": "westus2", + "name": "mySearchServicexxx", + "properties": { + "status": "running", + "provisioningState": "succeeded", + "replicaCount": 1, + "partitionCount": 1, + "sku": { + "name": "basic" + } + }, + "type": "Microsoft.Search/searchServices" + } + ``` + +4. **Create an Azure OpenAI Service** + + ```bash + export OPENAI_SERVICE_NAME="myOpenAIService$RANDOM_SUFFIX" + az cognitiveservices account create \ + --name $OPENAI_SERVICE_NAME \ + --resource-group $RESOURCE_GROUP \ + --kind OpenAI \ + --sku S0 \ + --location $LOCATION \ + --custom-domain $OPENAI_SERVICE_NAME + ``` + +## Step 2: Prepare the Data and Index + +1. **Create a Sample Document** + + ```bash + mkdir rag-chat-app + cd rag-chat-app + echo "Azure Cognitive Search enhances the experience of users by indexing and retrieving relevant data." > documents.txt + ``` + +2. **Upload Documents to Azure Cognitive Search** + + ```bash + az search service update \ + --name $SEARCH_SERVICE_NAME \ + --resource-group $RESOURCE_GROUP \ + --set properties.corsOptions.allowedOrigins="*" + + export SEARCH_ADMIN_KEY=$(az search admin-key show --resource-group $RESOURCE_GROUP --service-name $SEARCH_SERVICE_NAME --query primaryKey --output tsv) + ``` + + Create a Python script `upload_docs.py`: + + ```python + import os + from azure.core.credentials import AzureKeyCredential + from azure.search.documents import SearchClient, SearchIndexClient + from azure.search.documents.indexes.models import SearchIndex, SimpleField, edm + + search_service_endpoint = f"https://{os.environ['SEARCH_SERVICE_NAME']}.search.windows.net" + admin_key = os.environ['SEARCH_ADMIN_KEY'] + + index_name = "documents" + + index_client = SearchIndexClient(search_service_endpoint, AzureKeyCredential(admin_key)) + + fields = [ + SimpleField(name="id", type=edm.String, key=True), + SimpleField(name="content", type=edm.String, searchable=True) + ] + + index = SearchIndex(name=index_name, fields=fields) + + index_client.create_or_update_index(index) + + search_client = SearchClient(search_service_endpoint, index_name, AzureKeyCredential(admin_key)) + + documents = [ + {"id": "1", "content": open("documents.txt").read()} + ] + + result = search_client.upload_documents(documents) + print(f"Uploaded documents: {result}") + ``` + + Run the script: + + ```bash + export SEARCH_SERVICE_NAME + export SEARCH_ADMIN_KEY + python3 upload_docs.py + ``` + +## Step 3: Build the RAG Chat App + +1. **Create a Virtual Environment** + + ```bash + python3 -m venv venv + source venv/bin/activate + ``` + +2. **Install Dependencies** + + Create a `requirements.txt` file: + + ```plaintext + azure-search-documents + openai + python-dotenv + flask + ``` + + Install the dependencies: + + ```bash + pip install -r requirements.txt + ``` + +3. **Create the `app.py` File** + + ```python + import os + from flask import Flask, request, jsonify + from azure.core.credentials import AzureKeyCredential + from azure.search.documents import SearchClient + import openai + + app = Flask(__name__) + + search_service_endpoint = f"https://{os.environ['SEARCH_SERVICE_NAME']}.search.windows.net" + index_name = "documents" + search_client = SearchClient(search_service_endpoint, index_name, AzureKeyCredential(os.environ['SEARCH_ADMIN_KEY'])) + + openai.api_type = "azure" + openai.api_base = f"https://{os.environ['OPENAI_SERVICE_NAME']}.openai.azure.com/" + openai.api_version = "2023-03-15-preview" + openai.api_key = os.environ["OPENAI_API_KEY"] + + @app.route('/chat', methods=['POST']) + def chat(): + user_question = request.json.get('question', '') + + results = search_client.search(user_question) + context = " ".join([doc['content'] for doc in results]) + + response = openai.Completion.create( + engine="text-davinci-003", + prompt=f"Answer the following question using the context below:\n\nContext: {context}\n\nQuestion: {user_question}\nAnswer:", + max_tokens=150 + ) + + answer = response.choices[0].text.strip() + return jsonify({'answer': answer}) + + if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000) + ``` + +4. **Set Environment Variables** + + ```bash + export SEARCH_SERVICE_NAME=$SEARCH_SERVICE_NAME + export SEARCH_ADMIN_KEY=$SEARCH_ADMIN_KEY + export OPENAI_SERVICE_NAME=$OPENAI_SERVICE_NAME + export OPENAI_API_KEY="" + ``` + +## Step 4: Test the Application Locally + +Run the application: + +```bash +python3 app.py +``` + +Results: + + + +```log + * Serving Flask app 'app' + * Running on all addresses. + WARNING: This is a development server. Do not use it in a production deployment. + * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) +``` + +In another terminal, test the chat endpoint: + +```bash +curl -X POST http://localhost:5000/chat -H "Content-Type: application/json" -d '{"question": "What does Azure Cognitive Search do?"}' +``` + +Results: + + + +```JSON +{ + "answer": "Azure Cognitive Search indexes and retrieves relevant data to enhance user experiences." +} +``` + +## Step 5: (Optional) Containerize the Application + +1. **Create a `Dockerfile`** + + ```dockerfile + FROM python:3.9-slim + + WORKDIR /app + + COPY . /app + + RUN pip install --no-cache-dir -r requirements.txt + + EXPOSE 5000 + + CMD ["python", "app.py"] + ``` + +2. **Build the Docker Image** + + ```bash + docker build -t rag-chat-app . + ``` + +3. **Run the Docker Container** + + ```bash + docker run -p 5000:5000 rag-chat-app + ``` + +## Conclusion + +You have successfully created a RAG Chat App using Azure AI Search with OpenAI in Python. \ No newline at end of file diff --git a/scenarios/AIChatApp/app.py b/scenarios/AIChatApp/app.py new file mode 100644 index 000000000..066f1b913 --- /dev/null +++ b/scenarios/AIChatApp/app.py @@ -0,0 +1,37 @@ +import os +from langchain.document_loaders import TextLoader +from langchain.indexes import VectorstoreIndexCreator +from langchain.chains import ConversationalRetrievalChain +from langchain.embeddings import OpenAIEmbeddings +from langchain.llms import OpenAI +import chainlit as cl + +# Set Azure OpenAI API credentials +os.environ["OPENAI_API_TYPE"] = "azure" +os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY") +os.environ["OPENAI_API_BASE"] = os.getenv("OPENAI_API_BASE") +os.environ["OPENAI_API_VERSION"] = "2023-03-15-preview" + +# Load documents +loader = TextLoader('documents.txt') +documents = loader.load() + +# Create index +index = VectorstoreIndexCreator().from_loaders([loader]) + +# Create conversational retrieval chain +retriever = index.vectorstore.as_retriever() +qa_chain = ConversationalRetrievalChain.from_llm( + llm=OpenAI(temperature=0), + retriever=retriever +) + +# Initialize conversation history +history = [] + +@cl.on_message +async def main(message): + global history + result = qa_chain({"question": message, "chat_history": history}) + history.append((message, result['answer'])) + await cl.Message(content=result['answer']).send() \ No newline at end of file diff --git a/scenarios/AIChatApp/requirements.txt b/scenarios/AIChatApp/requirements.txt new file mode 100644 index 000000000..6bfe7c298 --- /dev/null +++ b/scenarios/AIChatApp/requirements.txt @@ -0,0 +1,5 @@ +langchain +chromadb +chainlit +openai +tiktoken \ No newline at end of file diff --git a/scenarios/ConfigurePythonContainer/configure-python-container.md b/scenarios/ConfigurePythonContainer/configure-python-container.md new file mode 100644 index 000000000..4ef412bc2 --- /dev/null +++ b/scenarios/ConfigurePythonContainer/configure-python-container.md @@ -0,0 +1,207 @@ +--- +title: 'Quickstart: Configure a Linux Python app in Azure App Service' +description: Learn how to configure a Linux Python app in Azure App Service, including setting Python versions and customizing build automation. +ms.topic: quickstart +ms.date: 10/07/2023 +author: msangapu +ms.author: msangapu +ms.custom: innovation-engine, devx-track-python, devx-track-azurecli, linux-related-content +--- + +# Quickstart: Configure a Linux Python app in Azure App Service + +In this quickstart, you'll learn how to configure a Python app deployed on Azure App Service using the Azure CLI. This includes setting and checking the Python version, listing the supported Python versions for App Service, and customizing build automation during deployment. + +## Prerequisites + +Ensure you have the following: + +- An Azure subscription. +- [Azure CLI installed](https://learn.microsoft.com/cli/azure/install-azure-cli) locally or access to [Azure Cloud Shell](https://ms.portal.azure.com/#cloudshell/). +- Permissions to manage resources in your Azure subscription. + +## Step 1: Create necessary resources + +The following commands create the required resources: a resource group, an App Service plan, and an App Service instance. **Random suffixes are included for resource names to avoid conflicts.** + +### Create a resource group + +```bash +export RANDOM_SUFFIX=$(openssl rand -hex 3) +export REGION="centralindia" +export RESOURCE_GROUP="MyResourceGroup$RANDOM_SUFFIX" +az group create --name $RESOURCE_GROUP --location $REGION +``` + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxx-xxxxx-xxxxx-xxxxx/resourceGroups/MyResourceGroupxxx", + "location": "centralindia", + "managedBy": null, + "name": "MyResourceGroupxxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +### Create an App Service plan + +```bash +export APP_SERVICE_PLAN="MyAppServicePlan$RANDOM_SUFFIX" +az appservice plan create --name $APP_SERVICE_PLAN --resource-group $RESOURCE_GROUP --sku FREE --is-linux +``` + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxx-xxxxx-xxxxx-xxxxx/resourceGroups/MyResourceGroupxxx/providers/Microsoft.Web/serverfarms/MyAppServicePlanxxx", + "location": "centralindia", + "name": "MyAppServicePlanxxx", + "sku": { + "name": "F1", + "tier": "Free", + "size": "F1", + "family": "F", + "capacity": 1 + }, + "reserved": true +} +``` + +### Create an App Service instance + +```bash +export APP_NAME="MyPythonApp$RANDOM_SUFFIX" +export RUNTIME="PYTHON|3.10" +az webapp create --resource-group $RESOURCE_GROUP --plan $APP_SERVICE_PLAN --name $APP_NAME --runtime $RUNTIME +``` + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxx-xxxxx-xxxxx-xxxxx/resourceGroups/MyResourceGroupxxx/providers/Microsoft.Web/sites/MyPythonAppxxx", + "name": "MyPythonAppxxx", + "state": "Running", + "defaultHostName": "MyPythonAppxxx.azurewebsites.net" +} +``` + +## Show the current Python version + +The following command retrieves the Python runtime version currently used by your Azure App Service. + +```bash +az webapp config show --resource-group $RESOURCE_GROUP --name $APP_NAME --query linuxFxVersion -o jsonc +``` + +Results: + + + +```jsonc +"PYTHON|3.10" +``` + +## Set the desired Python version + +Update your Azure App Service instance to use a specific Python version. Replace the desired Python version (e.g., "PYTHON|3.11") as needed. + +```bash +export DESIRED_PYTHON_VERSION="PYTHON|3.11" +az webapp config set --resource-group $RESOURCE_GROUP --name $APP_NAME --linux-fx-version $DESIRED_PYTHON_VERSION +``` + +## Verify Version +Verify the updated Python version: + +```bash +az webapp config show --resource-group $RESOURCE_GROUP --name $APP_NAME --query linuxFxVersion -o jsonc +``` + +Results: + + + +```jsonc +"PYTHON|3.11" +``` + +## List all supported Python runtime versions + +Use the following command to view all Python versions supported by Azure App Service on Linux. + +```bash +az webapp list-runtimes --os linux --query "[?contains(@, 'PYTHON')]" -o jsonc +``` + +Results: + + + +```jsonc +[ + "PYTHON|3.7", + "PYTHON|3.8", + "PYTHON|3.9", + "PYTHON|3.10", + "PYTHON|3.11" +] +``` + +## Step 5: Customize build automation + +Azure App Service automates the Python app-building process during deployment. These steps demonstrate how to configure or modify its behavior. + +### Enable build automation + +The following command configures App Service to run the build process during deployment by setting the `SCM_DO_BUILD_DURING_DEPLOYMENT` variable to `1`. + +```bash +az webapp config appsettings set --resource-group $RESOURCE_GROUP --name $APP_NAME --settings SCM_DO_BUILD_DURING_DEPLOYMENT="1" +``` + +## Step 6: Add application settings + +App settings in Azure App Service act as environment variables within your app. Below, we add and verify a sample setting. + +### Add a new App Service environment variable + +For example, set a `DATABASE_SERVER` variable for your app as shown below: + +```bash +export DATABASE_SERVER="https://mydatabase.example" +az webapp config appsettings set --resource-group $RESOURCE_GROUP --name $APP_NAME --settings DATABASE_SERVER=$DATABASE_SERVER +``` + +### Verify the setting + +```bash +az webapp config appsettings list --resource-group $RESOURCE_GROUP --name $APP_NAME --query "[?name=='DATABASE_SERVER']" -o jsonc +``` + +Results: + + + +```jsonc +[ + { + "name": "DATABASE_SERVER", + "slotSetting": false, + "value": "https://mydatabase.example" + } +] +``` \ No newline at end of file diff --git a/scenarios/CreateAKSWebApp/README.md b/scenarios/CreateAKSWebApp/README.md index 8888f5bc7..f3527485f 100644 --- a/scenarios/CreateAKSWebApp/README.md +++ b/scenarios/CreateAKSWebApp/README.md @@ -14,31 +14,14 @@ ms.custom: innovation-engine Welcome to this tutorial where we will take you step by step in creating an Azure Kubernetes Web Application that is secured via https. This tutorial assumes you are logged into Azure CLI already and have selected a subscription to use with the CLI. It also assumes that you have Helm installed ([Instructions can be found here](https://helm.sh/docs/intro/install/)). -## Define Environment Variables +## Create a resource group -The first step in this tutorial is to define environment variables. +A resource group is a container for related resources. All resources must be placed in a resource group. We will create one for this tutorial. The following command creates a resource group with the previously defined $MY_RESOURCE_GROUP_NAME and $REGION parameters. ```bash export RANDOM_ID="$(openssl rand -hex 3)" -export NETWORK_PREFIX="$(($RANDOM % 254 + 1))" -export SSL_EMAIL_ADDRESS="$(az account show --query user.name --output tsv)" export MY_RESOURCE_GROUP_NAME="myAKSResourceGroup$RANDOM_ID" export REGION="westeurope" -export MY_AKS_CLUSTER_NAME="myAKSCluster$RANDOM_ID" -export MY_PUBLIC_IP_NAME="myPublicIP$RANDOM_ID" -export MY_DNS_LABEL="mydnslabel$RANDOM_ID" -export MY_VNET_NAME="myVNet$RANDOM_ID" -export MY_VNET_PREFIX="10.$NETWORK_PREFIX.0.0/16" -export MY_SN_NAME="mySN$RANDOM_ID" -export MY_SN_PREFIX="10.$NETWORK_PREFIX.0.0/22" -export FQDN="${MY_DNS_LABEL}.${REGION}.cloudapp.azure.com" -``` - -## Create a resource group - -A resource group is a container for related resources. All resources must be placed in a resource group. We will create one for this tutorial. The following command creates a resource group with the previously defined $MY_RESOURCE_GROUP_NAME and $REGION parameters. - -```bash az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION ``` @@ -65,6 +48,11 @@ Results: A virtual network is the fundamental building block for private networks in Azure. Azure Virtual Network enables Azure resources like VMs to securely communicate with each other and the internet. ```bash +export NETWORK_PREFIX="$(($RANDOM % 254 + 1))" +export MY_VNET_NAME="myVNet$RANDOM_ID" +export MY_VNET_PREFIX="10.$NETWORK_PREFIX.0.0/16" +export MY_SN_NAME="mySN$RANDOM_ID" +export MY_SN_PREFIX="10.$NETWORK_PREFIX.0.0/22" az network vnet create \ --resource-group $MY_RESOURCE_GROUP_NAME \ --location $REGION \ @@ -129,6 +117,7 @@ This will take a few minutes. ```bash export MY_SN_ID=$(az network vnet subnet list --resource-group $MY_RESOURCE_GROUP_NAME --vnet-name $MY_VNET_NAME --query "[0].id" --output tsv) +export MY_AKS_CLUSTER_NAME="myAKSCluster$RANDOM_ID" az aks create \ --resource-group $MY_RESOURCE_GROUP_NAME \ --name $MY_AKS_CLUSTER_NAME \ @@ -176,6 +165,8 @@ kubectl get nodes ## Install NGINX Ingress Controller ```bash +export MY_PUBLIC_IP_NAME="myPublicIP$RANDOM_ID" +export MY_DNS_LABEL="mydnslabel$RANDOM_ID" export MY_STATIC_IP=$(az network public-ip create --resource-group MC_${MY_RESOURCE_GROUP_NAME}_${MY_AKS_CLUSTER_NAME}_${REGION} --location ${REGION} --name ${MY_PUBLIC_IP_NAME} --dns-name ${MY_DNS_LABEL} --sku Standard --allocation-method static --version IPv4 --zone 1 2 3 --query publicIp.ipAddress -o tsv) helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update @@ -458,6 +449,7 @@ while [[ $(date -u +%s) -le $endtime ]]; do fi; done +export FQDN="${MY_DNS_LABEL}.${REGION}.cloudapp.azure.com" curl "http://$FQDN" ``` @@ -488,92 +480,70 @@ Helm is a Kubernetes deployment tool for automating creation, packaging, configu Cert-manager provides Helm charts as a first-class method of installation on Kubernetes. -```bash -# Add the Jetstack Helm repository -# This repository is the only supported source of cert-manager charts. There are some other mirrors and copies across the internet, but those are entirely unofficial and could present a security risk. +1. Add the Jetstack Helm repository -helm repo add jetstack https://charts.jetstack.io + This repository is the only supported source of cert-manager charts. There are some other mirrors and copies across the internet, but those are entirely unofficial and could present a security risk. -# Update local Helm Chart repository cache -helm repo update + ```bash + helm repo add jetstack https://charts.jetstack.io + helm repo update + helm install cert-manager jetstack/cert-manager --namespace cert-manager --version v1.7.0 + ``` -# Install Cert-Manager addon via helm by running the following -helm install cert-manager jetstack/cert-manager --namespace cert-manager --version v1.7.0 +2. Update local Helm Chart repository cache -# ClusterIssuers are Kubernetes resources that represent certificate authorities (CAs) that are able to generate signed certificates by honoring certificate signing requests. All cert-manager certificates require a referenced issuer that is in a ready condition to attempt to honor the request. -# The issuer we are using can be found in the `cluster-issuer-prod.yml file` - -cat < cluster-issuer-prod.yml -apiVersion: cert-manager.io/v1 -kind: ClusterIssuer -metadata: - name: letsencrypt-prod -spec: - acme: - # You must replace this email address with your own. - # Let's Encrypt will use this to contact you about expiring - # certificates, and issues related to your account. - email: $SSL_EMAIL_ADDRESS - # ACME server URL for Let’s Encrypt’s prod environment. - # The staging environment will not issue trusted certificates but is - # used to ensure that the verification process is working properly - # before moving to production - server: https://acme-v02.api.letsencrypt.org/directory - # Secret resource used to store the account's private key. - privateKeySecretRef: - name: letsencrypt - # Enable the HTTP-01 challenge provider - # you prove ownership of a domain by ensuring that a particular - # file is present at the domain - solvers: - - http01: - ingress: - class: nginx - podTemplate: - spec: - nodeSelector: - "kubernetes.io/os": linux -EOF + ```bash + ``` -cluster_issuer_variables=$( azure-vote-nginx-ssl.yml ---- -# INGRESS WITH SSL PROD -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: vote-ingress - namespace: default - annotations: - kubernetes.io/tls-acme: "true" - nginx.ingress.kubernetes.io/ssl-redirect: "true" - cert-manager.io/cluster-issuer: letsencrypt-prod -spec: - ingressClassName: nginx - tls: - - hosts: - - $FQDN - secretName: azure-vote-nginx-secret - rules: - - host: $FQDN - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: azure-vote-front - port: - number: 80 -EOF +4. Apply Certificate Issuer YAML File -azure_vote_nginx_ssl_variables=$( cluster-issuer-prod.yml + apiVersion: cert-manager.io/v1 + kind: ClusterIssuer + metadata: + name: letsencrypt-prod + spec: + acme: + # You must replace this email address with your own. + # Let's Encrypt will use this to contact you about expiring + # certificates, and issues related to your account. + email: $SSL_EMAIL_ADDRESS + # ACME server URL for Let’s Encrypt’s prod environment. + # The staging environment will not issue trusted certificates but is + # used to ensure that the verification process is working properly + # before moving to production + server: https://acme-v02.api.letsencrypt.org/directory + # Secret resource used to store the account's private key. + privateKeySecretRef: + name: letsencrypt + # Enable the HTTP-01 challenge provider + # you prove ownership of a domain by ensuring that a particular + # file is present at the domain + solvers: + - http01: + ingress: + class: nginx + podTemplate: + spec: + nodeSelector: + "kubernetes.io/os": linux + EOF + cluster_issuer_variables=$(Create one for free. +- Access granted to Azure OpenAI in the desired Azure subscription. +- Access permissions to [create Azure OpenAI resources and to deploy models](../how-to/role-based-access-control.md). +- The Azure CLI. For more information, see [How to install the Azure CLI](/cli/azure/install-azure-cli). + +> [!NOTE] +> Currently, you must submit an application to access Azure OpenAI Service. To apply for access, complete [this form](https://aka.ms/oai/access). If you need assistance, open an issue on this repository to contact Microsoft. + +## Sign in to the Azure CLI + +[Sign in](/cli/azure/authenticate-azure-cli) to the Azure CLI or select **Open Cloudshell** in the following steps. + +## Create an Azure resource group + +To create an Azure OpenAI resource, you need an Azure resource group. When you create a new resource through the Azure CLI, you can also create a new resource group or instruct Azure to use an existing group. The following example shows how to create a new resource group named _$MY_RESOURCE_GROUP_NAME_ with the [az group create](/cli/azure/group?view=azure-cli-latest&preserve-view=true#az-group-create) command. The resource group is created in the East US region as defined by the enviornment variable _$REGION_. + +```bash +export RANDOM_ID="$(openssl rand -hex 3)" +export MY_RESOURCE_GROUP_NAME="myAOAIResourceGroup$RANDOM_ID" +export REGION="eastus" +export TAGS="owner=user" + +az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION --tags $TAGS +``` + +Results: + +```JSON +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myAOAIResourceGroupxxxxxx", + "location": "eastus", + "managedBy": null, + "name": "myAIResourceGroupxxxxxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": { + "owner": "user" + }, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Create a resource + +Use the [az cognitiveservices account create](/cli/azure/cognitiveservices/account?view=azure-cli-latest&preserve-view=true#az-cognitiveservices-account-create) command to create an Azure OpenAI resource in the resource group. In the following example, you create a resource named _$MY_OPENAI_RESOURCE_NAME_ in the _$MY_RESOURCE_GROUP_NAME_ resource group. When you try the example, update the environment variables to use your desired values for the resource group and resource name. + +```bash +export MY_OPENAI_RESOURCE_NAME="myOAIResource$RANDOM_ID" +az cognitiveservices account create \ +--name $MY_OPENAI_RESOURCE_NAME \ +--resource-group $MY_RESOURCE_GROUP_NAME \ +--location $REGION \ +--kind OpenAI \ +--sku s0 \ +``` +Results: + +```JSON +{ + "etag": "\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myAOAIResourceGroupxxxxxx/providers/Microsoft.CognitiveServices/accounts/myOAIResourcexxxxxx", + "identity": null, + "kind": "OpenAI", + "location": "eastus", + "name": "myOAIResourcexxxxxx", + "properties": { + "abusePenalty": null, + "allowedFqdnList": null, + "apiProperties": null, + "callRateLimit": { + "count": null, + "renewalPeriod": null, + "rules": [ + { + "count": 30.0, + "dynamicThrottlingEnabled": null, + "key": "openai.dalle.post", + "matchPatterns": [ + { + "method": "POST", + "path": "dalle/*" + }, + { + "method": "POST", + "path": "openai/images/*" + } + ], + "minCount": null, + "renewalPeriod": 1.0 + }, + { + "count": 30.0, + "dynamicThrottlingEnabled": null, + "key": "openai.dalle.other", + "matchPatterns": [ + { + "method": "*", + "path": "dalle/*" + }, + { + "method": "*", + "path": "openai/operations/images/*" + } + ], + "minCount": null, + "renewalPeriod": 1.0 + }, + { + "count": 30.0, + "dynamicThrottlingEnabled": null, + "key": "openai", + "matchPatterns": [ + { + "method": "*", + "path": "openai/*" + } + ], + "minCount": null, + "renewalPeriod": 1.0 + }, + { + "count": 30.0, + "dynamicThrottlingEnabled": null, + "key": "default", + "matchPatterns": [ + { + "method": "*", + "path": "*" + } + ], + "minCount": null, + "renewalPeriod": 1.0 + } + ] + }, + "capabilities": [ + { + "name": "VirtualNetworks", + "value": null + }, + { + "name": "CustomerManagedKey", + "value": null + }, + { + "name": "MaxFineTuneCount", + "value": "100" + }, + { + "name": "MaxRunningFineTuneCount", + "value": "1" + }, + { + "name": "MaxUserFileCount", + "value": "50" + }, + { + "name": "MaxTrainingFileSize", + "value": "512000000" + }, + { + "name": "MaxUserFileImportDurationInHours", + "value": "1" + }, + { + "name": "MaxFineTuneJobDurationInHours", + "value": "720" + }, + { + "name": "TrustedServices", + "value": "Microsoft.CognitiveServices,Microsoft.MachineLearningServices,Microsoft.Search" + } + ], + "commitmentPlanAssociations": null, + "customSubDomainName": null, + "dateCreated": "xxxx-xx-xxxxx:xx:xx.xxxxxxxx", + "deletionDate": null, + "disableLocalAuth": null, + "dynamicThrottlingEnabled": null, + "encryption": null, + "endpoint": "https://eastus.api.cognitive.microsoft.com/", + "endpoints": { + "OpenAI Dall-E API": "https://eastus.api.cognitive.microsoft.com/", + "OpenAI Language Model Instance API": "https://eastus.api.cognitive.microsoft.com/", + "OpenAI Model Scaleset API": "https://eastus.api.cognitive.microsoft.com/", + "OpenAI Whisper API": "https://eastus.api.cognitive.microsoft.com/" + }, + "internalId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "isMigrated": false, + "locations": null, + "migrationToken": null, + "networkAcls": null, + "privateEndpointConnections": [], + "provisioningState": "Succeeded", + "publicNetworkAccess": "Enabled", + "quotaLimit": null, + "restore": null, + "restrictOutboundNetworkAccess": null, + "scheduledPurgeDate": null, + "skuChangeInfo": null, + "userOwnedStorage": null + }, + "resourceGroup": "myAOAIResourceGroupxxxxxx", + "sku": { + "capacity": null, + "family": null, + "name": "S0", + "size": null, + "tier": null + }, + "systemData": { + "createdAt": "xxxx-xx-xxxxx:xx:xx.xxxxxxxx", + "createdBy": "yyyyyyyyyyyyyyyyyyyyyyyy", + "createdByType": "User", + "lastModifiedAt": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx", + "lastModifiedBy": "yyyyyyyyyyyyyyyyyyyyyyyy", + "lastModifiedByType": "User" + }, + "tags": null, + "type": "Microsoft.CognitiveServices/accounts" +} +``` + +## Retrieve information about the resource + +After you create the resource, you can use different commands to find useful information about your Azure OpenAI Service instance. The following examples demonstrate how to retrieve the REST API endpoint base URL and the access keys for the new resource. + +### Get the endpoint URL + +Use the [az cognitiveservices account show](/cli/azure/cognitiveservices/account?view=azure-cli-latest&preserve-view=true#az-cognitiveservices-account-show) command to retrieve the REST API endpoint base URL for the resource. In this example, we direct the command output through the [jq](https://jqlang.github.io/jq/) JSON processor to locate the `.properties.endpoint` value. + +When you try the example, update the environment variables to use your values for the resource group _$MY_RESOURCE_GROUP_NAME_ and resource _$MY_OPENAI_RESOURCE_NAME_. + +```bash +az cognitiveservices account show \ +--name $MY_OPENAI_RESOURCE_NAME \ +--resource-group $MY_RESOURCE_GROUP_NAME \ +| jq -r .properties.endpoint +``` + +### Get the primary API key + +To retrieve the access keys for the resource, use the [az cognitiveservices account keys list](/cli/azure/cognitiveservices/account?view=azure-cli-latest&preserve-view=true#az-cognitiveservices-account-keys-list) command. In this example, we direct the command output through the [jq](https://jqlang.github.io/jq/) JSON processor to locate the `.key1` value. + +When you try the example, update the environment variables to use your values for the resource group and resource. + +```bash +az cognitiveservices account keys list \ +--name $MY_OPENAI_RESOURCE_NAME \ +--resource-group $MY_RESOURCE_GROUP_NAME \ +| jq -r .key1 +``` + +## Deploy a model + +To deploy a model, use the [az cognitiveservices account deployment create](/cli/azure/cognitiveservices/account/deployment?view=azure-cli-latest&preserve-view=true#az-cognitiveservices-account-deployment-create) command. In the following example, you deploy an instance of the `text-embedding-ada-002` model and give it the name _$MY_MODEL_NAME_. When you try the example, update the variables to use your values for the resource group and resource. You don't need to change the `model-version`, `model-format` or `sku-capacity`, and `sku-name` values. + +```bash +export MY_MODEL_NAME="myModel$RANDOM_ID" +az cognitiveservices account deployment create \ +--name $MY_OPENAI_RESOURCE_NAME \ +--resource-group $MY_RESOURCE_GROUP_NAME \ +--deployment-name $MY_MODEL_NAME \ +--model-name text-embedding-ada-002 \ +--model-version "2" \ +--model-format OpenAI \ +--sku-capacity "1" \ +--sku-name "Standard" +``` + +`--sku-name` accepts the following deployment types: `Standard`, `GlobalStandard`, and `ProvisionedManaged`. Learn more about [deployment type options](../how-to/deployment-types.md). + + +> [!IMPORTANT] +> When you access the model via the API, you need to refer to the deployment name rather than the underlying model name in API calls, which is one of the [key differences](../how-to/switching-endpoints.yml) between OpenAI and Azure OpenAI. OpenAI only requires the model name. Azure OpenAI always requires deployment name, even when using the model parameter. In our docs, we often have examples where deployment names are represented as identical to model names to help indicate which model works with a particular API endpoint. Ultimately your deployment names can follow whatever naming convention is best for your use case. + +Results: + +```JSON +{ + "etag": "\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myAOAIResourceGroupxxxxxx/providers/Microsoft.CognitiveServices/accounts/myOAIResourcexxxxxx/deployments/myModelxxxxxx", + "name": "myModelxxxxxx", + "properties": { + "callRateLimit": null, + "capabilities": { + "embeddings": "true", + "embeddingsMaxInputs": "1" + }, + "model": { + "callRateLimit": null, + "format": "OpenAI", + "name": "text-embedding-ada-002", + "source": null, + "version": "1" + }, + "provisioningState": "Succeeded", + "raiPolicyName": null, + "rateLimits": [ + { + "count": 1.0, + "dynamicThrottlingEnabled": null, + "key": "request", + "matchPatterns": null, + "minCount": null, + "renewalPeriod": 10.0 + }, + { + "count": 1000.0, + "dynamicThrottlingEnabled": null, + "key": "token", + "matchPatterns": null, + "minCount": null, + "renewalPeriod": 60.0 + } + ], + "scaleSettings": null, + "versionUpgradeOption": "OnceNewDefaultVersionAvailable" + }, + "resourceGroup": "myAOAIResourceGroupxxxxxx", + "sku": { + "capacity": 1, + "family": null, + "name": "Standard", + "size": null, + "tier": null + }, + "systemData": { + "createdAt": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx", + "createdBy": "yyyyyyyyyyyyyyyyyyyyyyyy", + "createdByType": "User", + "lastModifiedAt": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx", + "lastModifiedBy": "yyyyyyyyyyyyyyyyyyyyyyyy", + "lastModifiedByType": "User" + }, + "type": "Microsoft.CognitiveServices/accounts/deployments" +} +``` +## Delete a model from your resource + +You can delete any model deployed from your resource with the [az cognitiveservices account deployment delete](/cli/azure/cognitiveservices/account/deployment?view=azure-cli-latest&preserve-view=true#az-cognitiveservices-account-deployment-delete) command. \ No newline at end of file diff --git a/scenarios/CreateContainerAppDeploymentFromSource/create-container-app-deployment-from-source.md b/scenarios/CreateContainerAppDeploymentFromSource/create-container-app-deployment-from-source.md new file mode 100644 index 000000000..5be70797f --- /dev/null +++ b/scenarios/CreateContainerAppDeploymentFromSource/create-container-app-deployment-from-source.md @@ -0,0 +1,636 @@ +--- +title: Create a Container App leveraging Blob Store, SQL, and Computer Vision +description: This tutorial shows how to create a Container App leveraging Blob Store, SQL, and Computer Vision +author: mbifeld +ms.author: mbifeld +ms.topic: article +ms.date: 12/06/2023 +ms.custom: innovation-engine +--- + +# Create a Container App leveraging Blob Store, SQL, and Computer Vision + +In this guide, we'll be walking through deploying the necessary resources for a web app that allows users to cast votes using their name, email and an image. Users can vote for their preference of cat or dog, using an image of a cat or a dog that will be analyzed by our infrastructure. For this to work, we will be deploying resources across several different Azure services: + +- **Azure Storage Account** to store the images +- **Azure Database for PostgreSQL** to store users and votes +- **Azure Computer Vision** to analyze the images for cats or dogs +- **Azure Container App** to deploy our code + +Note: If you've never created a Computer Vision resource before, you will not be able to create one using the Azure CLI. You must create your first Computer Vision resource from the Azure portal to review and acknowledge the Responsible AI terms and conditions. You can do so here: [Create a Computer Vision Resource](https://portal.azure.com/#create/Microsoft.CognitiveServicesComputerVision). After that, you can create subsequent resources using any deployment tool (SDK, CLI, or ARM template, etc) under the same Azure subscription. + +## Clone the sample repository + +First, we're going to clone this repository onto our local machines. This will provide the starter code required to provide the functionality for the simple application outlined above. We can clone with a simple git command. + +```bash +git clone https://github.com/Azure/computer-vision-nextjs-webapp.git +``` + +To preserve saved environment variables, it's important that this terminal window stays open for the duration of the deployment. + +## Login to Azure using the CLI + +In order to run commands against Azure using [the CLI ](https://learn.microsoft.com/cli/azure/install-azure-cli)you need to login. This is done though the `az login` command: + +## Create a resource group + +A resource group is a container for related resources. All resources must be placed in a resource group. We will create one for this tutorial. The following command creates a resource group with the previously defined $MY_RESOURCE_GROUP_NAME and $REGION parameters. + +```bash +export SUFFIX="$(openssl rand -hex 3)" +export MY_RESOURCE_GROUP_NAME=rg$SUFFIX +export REGION="eastus2" +az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION +``` + +Results: + + +```json +{ + "id": "/subscriptions/xxxxx-xxxxxx-xxxxxx-xxxxxx/resourceGroups/$MY_RESOURCE_GROUP_NAME", + "location": "$REGION", + "managedBy": null, + "name": "$MY_RESOURCE_GROUP_NAME", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Create the storage account + +To create a storage account in this resource group we need to run a simple command. To this command, we are passing the name of the storage account, the resource group to deploy it in, the physical region to deploy it in, and the SKU of the storage account. All values are configured using environment variables. + +```bash +export MY_STORAGE_ACCOUNT_NAME=storage$SUFFIX +az storage account create --name $MY_STORAGE_ACCOUNT_NAME --resource-group $MY_RESOURCE_GROUP_NAME --location $REGION --sku Standard_LRS +``` + +Results: + + +```json +{ + "accessTier": "Hot", + "allowBlobPublicAccess": false, + "allowCrossTenantReplication": null, + "allowSharedKeyAccess": null, + "allowedCopyScope": null, + "azureFilesIdentityBasedAuthentication": null, + "blobRestoreStatus": null, + "creationTime": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx", + "customDomain": null, + "defaultToOAuthAuthentication": null, + "dnsEndpointType": null, + "enableHttpsTrafficOnly": true, + "enableNfsV3": null, + "encryption": { + "encryptionIdentity": null, + "keySource": "Microsoft.Storage", + "keyVaultProperties": null, + "requireInfrastructureEncryption": null, + "services": { + "blob": { + "enabled": true, + "keyType": "Account", + "lastEnabledTime": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx" + }, + "file": { + "enabled": true, + "keyType": "Account", + "lastEnabledTime": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx" + }, + "queue": null, + "table": null + } + }, + "extendedLocation": null, + "failoverInProgress": null, + "geoReplicationStats": null, + "id": "/subscriptions/xxxxx-xxxxxx-xxxxxx-xxxxxx/resourceGroups/$MY_RESOURCE_GROUP_NAME/providers/Microsoft.Storage/storageAccounts/$MY_STORAGE_ACCOUNT_NAME", + "identity": null, + "immutableStorageWithVersioning": null, + "isHnsEnabled": null, + "isLocalUserEnabled": null, + "isSftpEnabled": null, + "keyCreationTime": { + "key1": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx", + "key2": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx" + }, + "keyPolicy": null, + "kind": "StorageV2", + "largeFileSharesState": null, + "lastGeoFailoverTime": null, + "location": "$REGION", + "minimumTlsVersion": "TLS1_0", + "name": "$MY_STORAGE_ACCOUNT_NAME", + "networkRuleSet": { + "bypass": "AzureServices", + "defaultAction": "Allow", + "ipRules": [], + "resourceAccessRules": null, + "virtualNetworkRules": [] + }, + "primaryEndpoints": { + "blob": "https://$MY_STORAGE_ACCOUNT_NAME.blob.core.windows.net/", + "dfs": "https://$MY_STORAGE_ACCOUNT_NAME.dfs.core.windows.net/", + "file": "https://$MY_STORAGE_ACCOUNT_NAME.file.core.windows.net/", + "internetEndpoints": null, + "microsoftEndpoints": null, + "queue": "https://$MY_STORAGE_ACCOUNT_NAME.queue.core.windows.net/", + "table": "https://$MY_STORAGE_ACCOUNT_NAME.table.core.windows.net/", + "web": "https://$MY_STORAGE_ACCOUNT_NAME.z22.web.core.windows.net/" + }, + "primaryLocation": "$REGION", + "privateEndpointConnections": [], + "provisioningState": "Succeeded", + "publicNetworkAccess": null, + "resourceGroup": "$MY_RESOURCE_GROUP_NAME", + "routingPreference": null, + "sasPolicy": null, + "secondaryEndpoints": null, + "secondaryLocation": null, + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "statusOfPrimary": "available", + "statusOfSecondary": null, + "storageAccountSkuConversionStatus": null, + "tags": {}, + "type": "Microsoft.Storage/storageAccounts" +} +``` + +We also need to store one of the API keys for the storage account into an environment variable for later use (to create a container, and put it into an environment file for the code). We are calling the `keys list` command on the storage account and storing the first one in a `STORAGE_ACCOUNT_KEY` environment variable. + +```bash +export STORAGE_ACCOUNT_KEY=$(az storage account keys list --account-name $MY_STORAGE_ACCOUNT_NAME --resource-group $MY_RESOURCE_GROUP_NAME --query "[0].value" --output tsv) +``` + +## Create a container in the storage account + +Run the following command to create an `images` container in the storage account we just created. User uploaded images will be stored as blobs in this container. + +```bash +az storage container create --name images --account-name $MY_STORAGE_ACCOUNT_NAME --account-key $STORAGE_ACCOUNT_KEY --public-access blob +``` + +Results: + + +```json +{ + "created": true +} +``` + +## Create a database + +We will be creating an Azure Database for PostgreSQL flexible server for the application to store users and their votes. We are passing several arguments to the `create` command: + +- The basics: database name, resource group, and physical region to deploy in. +- The tier (which determines the capabilities of the server) as `burstable`, which is for workloads that don't need full CPU continuously. +- The SKU as `Standard_B1ms`. + - `Standard` for the performance tier. + - `B` for burstable workload. + - `1` for a single vCore. + - `ms` for memory optimized. +- The storage size, 32 GiB +- The PostgreSQL major version, 15 +- The datatabase credentials: username and password + +```bash +export MY_DATABASE_SERVER_NAME=dbserver$SUFFIX +export MY_DATABASE_NAME=db$SUFFIX +export MY_DATABASE_USERNAME=dbuser$SUFFIX +export MY_DATABASE_PASSWORD=dbpass$SUFFIX +az postgres flexible-server create \ + --name $MY_DATABASE_SERVER_NAME \ + --database-name $MY_DATABASE_NAME \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --location $REGION \ + --tier Burstable \ + --sku-name Standard_B1ms \ + --storage-size 32 \ + --version 15 \ + --admin-user $MY_DATABASE_USERNAME \ + --admin-password $MY_DATABASE_PASSWORD \ + --yes +``` + +Results: + + +```json +{ + "connectionString": "postgresql://$MY_DATABASE_USERNAME:$MY_DATABASE_PASSWORD@$MY_DATABASE_NAME.postgres.database.azure.com/flexibleserverdb?sslmode=require", + "databaseName": "$MY_DATABASE_NAME", + "firewallName": "FirewallIPAddress_xxxx-xx-xx-xx-xx", + "host": "$MY_DATABASE_NAME.postgres.database.azure.com", + "id": "/subscriptions/xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx/resourceGroups/$MY_RESOURCE_GROUP_NAME/providers/Microsoft.DBforPostgreSQL/flexibleServers/$MY_DATABASE_NAME", + "location": "$REGION", + "password": "$MY_DATABASE_PASSWORD", + "resourceGroup": "$MY_RESOURCE_GROUP_NAME", + "skuname": "Standard_B1ms", + "username": "$MY_DATABASE_USERNAME", + "version": "15" +} +``` + +We also need to store the connection string to the database into an environment variable for later use. This URL will allow us to access the database within the resource we just created. + +```bash +export DATABASE_URL="postgres://$MY_DATABASE_USERNAME:$MY_DATABASE_PASSWORD@$MY_DATABASE_SERVER_NAME.postgres.database.azure.com/$MY_DATABASE_NAME" +``` + +## Create a Computer Vision resource + +We will be creating a Computer Vision resource to be able to identify cats or dogs in the pictures users upload. Creating a Computer Vision resource can be done with a single command. We are passing several arguments to the `create` command: + +- The basics: resource name, resource group, the region, and to create a Computer Vision resource. +- The SKU as `S1`, or the most cost-effective paid performance tier. + +```bash +export MY_COMPUTER_VISION_NAME=computervision$SUFFIX + +az cognitiveservices account create \ + --name $MY_COMPUTER_VISION_NAME \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --location $REGION \ + --kind ComputerVision \ + --sku S1 \ + --yes +``` + +Results: + + +```json +{ + "etag": "xxxxxxx-xxxxxx-xxxxxxx-xxxxxxxxxx", + "id": "/subscriptions/xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx/resourceGroups/$MY_RESOURCE_GROUP_NAME/providers/Microsoft.CognitiveServices/accounts/$MY_COMPUTER_VISION_NAME", + "identity": null, + "kind": "ComputerVision", + "location": "$REGION", + "name": "$MY_COMPUTER_VISION_NAME", + "properties": { + "allowedFqdnList": null, + "apiProperties": null, + "callRateLimit": { + "count": null, + "renewalPeriod": null, + "rules": [ + { + "count": 30.0, + "dynamicThrottlingEnabled": true, + "key": "vision.recognizeText", + "matchPatterns": [ + { + "method": "POST", + "path": "vision/recognizeText" + }, + { + "method": "GET", + "path": "vision/textOperations/*" + }, + { + "method": "*", + "path": "vision/read/*" + } + ], + "minCount": null, + "renewalPeriod": 1.0 + }, + { + "count": 15.0, + "dynamicThrottlingEnabled": true, + "key": "vision", + "matchPatterns": [ + { + "method": "*", + "path": "vision/*" + } + ], + "minCount": null, + "renewalPeriod": 1.0 + }, + { + "count": 500.0, + "dynamicThrottlingEnabled": null, + "key": "container.billing", + "matchPatterns": [ + { + "method": "*", + "path": "billing/*" + } + ], + "minCount": null, + "renewalPeriod": 10.0 + }, + { + "count": 20.0, + "dynamicThrottlingEnabled": true, + "key": "default", + "matchPatterns": [ + { + "method": "*", + "path": "*" + } + ], + "minCount": null, + "renewalPeriod": 1.0 + } + ] + }, + "capabilities": [ + { + "name": "DynamicThrottling", + "value": null + }, + { + "name": "VirtualNetworks", + "value": null + }, + { + "name": "Container", + "value": "ComputerVision.VideoAnalytics,ComputerVision.ComputerVisionRead,ComputerVision.ocr,ComputerVision.readfile,ComputerVision.readfiledsd,ComputerVision.recognizetext,ComputerVision.ComputerVision,ComputerVision.ocrlayoutworker,ComputerVision.ocrcontroller,ComputerVision.ocrdispatcher,ComputerVision.ocrbillingprocessor,ComputerVision.ocranalyzer,ComputerVision.ocrpagesplitter,ComputerVision.ocrapi,ComputerVision.ocrengineworker" + } + ], + "customSubDomainName": null, + "dateCreated": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx", + "deletionDate": null, + "disableLocalAuth": null, + "dynamicThrottlingEnabled": null, + "encryption": null, + "endpoint": "https://$REGION.api.cognitive.microsoft.com/", + "endpoints": { + "Computer Vision": "https://$REGION.api.cognitive.microsoft.com/", + "Container": "https://$REGION.api.cognitive.microsoft.com/" + }, + "internalId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "isMigrated": false, + "migrationToken": null, + "networkAcls": null, + "privateEndpointConnections": [], + "provisioningState": "Succeeded", + "publicNetworkAccess": "Enabled", + "quotaLimit": null, + "restore": null, + "restrictOutboundNetworkAccess": null, + "scheduledPurgeDate": null, + "skuChangeInfo": null, + "userOwnedStorage": null + }, + "resourceGroup": "$MY_RESOURCE_GROUP_NAME", + "sku": { + "capacity": null, + "family": null, + "name": "S1", + "size": null, + "tier": null + }, + "systemData": { + "createdAt": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx", + "createdBy": "username@domain.com", + "createdByType": "User", + "lastModifiedAt": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx", + "lastModifiedBy": "username@domain.com", + "lastModifiedByType": "User" + }, + "tags": null, + "type": "Microsoft.CognitiveServices/accounts" +} +``` + +To access our computer vision resource, we need both the endpoint and the key. With the Azure CLI, we have access to two `az cognitiveservices account` commands: `show` and `keys list`, which give us what we need. + +```bash +export COMPUTER_VISION_ENDPOINT=$(az cognitiveservices account show --name $MY_COMPUTER_VISION_NAME --resource-group $MY_RESOURCE_GROUP_NAME --query "properties.endpoint" --output tsv) +export COMPUTER_VISION_KEY=$(az cognitiveservices account keys list --name $MY_COMPUTER_VISION_NAME --resource-group $MY_RESOURCE_GROUP_NAME --query "key1" --output tsv) +``` + +## Deploy the code into a Container App + +Now that we've got our storage, database, and Computer Vision resources all set up, we are ready to deploy the application code. To do this, we're going to use Azure Container Apps to host a containerized build of our Next.js app. The `Dockerfile` is already created at the root of the repository, so all we need to do is run a single command to deploy the code. + +This command will create an Azure Container Registry resource to host our Docker image, an Azure Container App resource which runs the image, and an Azure Container App Environment resource for our image. Let's break down what we're passing into the command. + +- The basics: resource name, resource group, and the region +- The name of the Azure Container App Environment resource to use or create +- The path to the source code + +```bash +export MY_CONTAINER_APP_NAME=containerapp$SUFFIX +export MY_CONTAINER_APP_ENV_NAME=containerappenv$SUFFIX + +az containerapp up \ + --name $MY_CONTAINER_APP_NAME \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --location $REGION \ + --environment $MY_CONTAINER_APP_ENV_NAME \ + --context-path computer-vision-nextjs-webapp \ + --source computer-vision-nextjs-webapp \ + --target-port 3000 \ + --ingress external \ + --env-vars \ + AZURE_DATABASE_URL=$DATABASE_URL \ + AZURE_COMPUTER_VISION_KEY=$COMPUTER_VISION_KEY \ + AZURE_COMPUTER_VISION_ENDPOINT=$COMPUTER_VISION_ENDPOINT \ + AZURE_STORAGE_ACCOUNT_NAME=$MY_STORAGE_ACCOUNT_NAME \ + AZURE_STORAGE_ACCOUNT_KEY=$STORAGE_ACCOUNT_KEY +``` + +We can verify that the command was successful by using: + +```bash +az containerapp show --name $MY_CONTAINER_APP_NAME --resource-group $MY_RESOURCE_GROUP_NAME +``` + +Results: + + +```json +{ + "id": "/subscriptions/xxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxxx/resourceGroups/$MY_RESOURCE_GROUP_NAME/providers/Microsoft.App/containerapps/$MY_CONTAINER_APP_NAME", + "identity": { + "type": "None" + }, + "location": "West US", + "name": "$MY_CONTAINER_APP_NAME", + "properties": { + "configuration": { + "activeRevisionsMode": "Single", + "dapr": null, + "ingress": { + "allowInsecure": false, + "clientCertificateMode": null, + "corsPolicy": null, + "customDomains": null, + "exposedPort": 0, + "external": true, + "fqdn": "$MY_CONTAINER_APP_NAME.xxxxxxx-xxxxxxxxxx.$REGION.azurecontainerapps.io", + "ipSecurityRestrictions": null, + "stickySessions": null, + "targetPort": 3000, + "traffic": [ + { + "latestRevision": true, + "weight": 100 + } + ], + "transport": "Auto" + }, + "maxInactiveRevisions": null, + "registries": null, + "secrets": null, + "service": null + }, + "customDomainVerificationId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "environmentId": "/subscriptions/xxxxxxxx-xxxxxxxx-xxxxxxxxx-xxxxxxxxx/resourceGroups/$MY_RESOURCE_GROUP_NAME/providers/Microsoft.App/managedEnvironments/$MY_CONTAINER_APP_ENV_NAME", + "eventStreamEndpoint": "https://$REGION.azurecontainerapps.dev/subscriptions/xxxxxxxx-xxxxxxxx-xxxxxxxxx-xxxxxxxxx/resourceGroups/$MY_RESOURCE_GROUP_NAME/containerApps/$MY_CONTAINER_APP_NAME/eventstream", + "latestReadyRevisionName": "$MY_CONTAINER_APP_NAME-xxxxxxx", + "latestRevisionFqdn": "$MY_CONTAINER_APP_NAME-xxxxxxx.kindocean-xxxxxxxx.$REGION.azurecontainerapps.io", + "latestRevisionName": "$MY_CONTAINER_APP_NAME-xxxxxxx", + "managedEnvironmentId": "/subscriptions/xxxxxxxx-xxxxxxxx-xxxxxxxxx-xxxxxxxxx/resourceGroups/$MY_RESOURCE_GROUP_NAME/providers/Microsoft.App/managedEnvironments/$MY_CONTAINER_APP_ENV_NAME", + "outboundIpAddresses": ["xx.xxx.xx.xxxx"], + "provisioningState": "Succeeded", + "runningStatus": "Running", + "template": { + "containers": [ + { + "env": [ + { + "name": "AZURE_DATABASE_URL", + "value": "$DATABASE_URL" + }, + { + "name": "AZURE_COMPUTER_VISION_KEY", + "value": "$COMPUTER_VISION_KEY" + }, + { + "name": "AZURE_COMPUTER_VISION_ENDPOINT", + "value": "$COMPUTER_VISION_ENDPOINT" + }, + { + "name": "AZURE_STORAGE_ACCOUNT_NAME", + "value": "$MY_STORAGE_ACCOUNT_NAME" + }, + { + "name": "AZURE_STORAGE_ACCOUNT_KEY", + "value": "$STORAGE_ACCOUNT_KEY" + } + ], + "image": "xxxxxx/xx-xxxx", + "name": "$MY_CONTAINER_APP_NAME", + "resources": { + "cpu": 0.5, + "ephemeralStorage": "2Gi", + "memory": "1Gi" + } + } + ], + "initContainers": null, + "revisionSuffix": "", + "scale": { + "maxReplicas": 10, + "minReplicas": null, + "rules": null + }, + "serviceBinds": null, + "terminationGracePeriodSeconds": null, + "volumes": null + }, + "workloadProfileName": null + }, + "resourceGroup": "$MY_RESOURCE_GROUP_NAME", + "systemData": { + "createdAt": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx", + "createdBy": "username@domain.com", + "createdByType": "User", + "lastModifiedAt": "xxxx-xx-xxxxx:xx:xx.xxxxxx+xx:xx", + "lastModifiedBy": "username@domain.com", + "lastModifiedByType": "User" + }, + "type": "Microsoft.App/containerApps" +} +``` + +## Create a database firewall rule + +By default, our database is configured to allow traffic from an allowlist of IP addresses. We need to add the IP of our newly deployed Container App to this allowlist. We can get the IP from the `az containerapp show` command. + +```bash +export CONTAINER_APP_IP=$(az containerapp show --name $MY_CONTAINER_APP_NAME --resource-group $MY_RESOURCE_GROUP_NAME --query "properties.outboundIpAddresses[0]" --output tsv) +``` + +We can now add this IP as a firewall rule with this command: + +```bash +az postgres flexible-server firewall-rule create \ + --name $MY_DATABASE_SERVER_NAME \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --rule-name allow-container-app \ + --start-ip-address $CONTAINER_APP_IP \ + --end-ip-address $CONTAINER_APP_IP +``` + +Results: + + +```json +{ + "endIpAddress": "xx.xxx.xx.xxx", + "id": "/subscriptions/xxxxxxxx-xxxxxxxx-xxxxxxx-xxxxxxx/resourceGroups/$MY_RESOURCE_GROUP_NAME/providers/Microsoft.DBforPostgreSQL/flexibleServers/$MY_DATABASE_SERVER_NAME/firewallRules/allow-container-app", + "name": "allow-container-app", + "resourceGroup": "$MY_RESOURCE_GROUP_NAME", + "startIpAddress": "xx.xxx.xx.xxx", + "systemData": null, + "type": "Microsoft.DBforPostgreSQL/flexibleServers/firewallRules" +} +``` + +## Create a storage CORS rule + +Web browsers implement a security restriction known as same-origin policy that prevents a web page from calling APIs in a different domain. CORS provides a secure way to allow one domain (the origin domain) to call APIs in another domain. We need to add a CORS rule on the URL of our web app to our storage account. First, let's get the URL with a similar `az containerapp show` command as earlier. + +```bash +export CONTAINER_APP_URL=https://$(az containerapp show --name $MY_CONTAINER_APP_NAME --resource-group $MY_RESOURCE_GROUP_NAME --query "properties.configuration.ingress.fqdn" --output tsv) +``` + +Next, we're ready to add a CORS rule with the following command. Let's break down the different parts of this command. + +- We are specifying blob service as the storage type to add the rule to. +- We are allowing all operations to be performed. +- We are allowing only the container app URL we just saved. +- We are allowing all HTTP headers from this URL. +- Max age is the amount of time, in seconds, that a browser should cache the preflight response for a specific request. +- We are passing the storage account name and key from earlier. + +```bash +az storage cors add \ + --services b \ + --methods DELETE GET HEAD MERGE OPTIONS POST PUT PATCH \ + --origins $CONTAINER_APP_URL \ + --allowed-headers '*' \ + --max-age 3600 \ + --account-name $MY_STORAGE_ACCOUNT_NAME \ + --account-key $STORAGE_ACCOUNT_KEY +``` + +That's it! Feel free to access the newly deployed web app in your browser printing the CONTAINER_APP_URL environment variable we added earlier. + +```bash +echo $CONTAINER_APP_URL +``` + +## Next Steps + +- [Azure Container Apps documentation](https://learn.microsoft.com/azure/container-apps/) +- [Azure Database for PostgreSQL documentation](https://learn.microsoft.com/azure/postgresql/) +- [Azure Blob Storage documentation](https://learn.microsoft.com/azure/storage/blobs/) +- [Azure Computer (AI) Vision Documentation](https://learn.microsoft.com/azure/ai-services/computer-vision/) diff --git a/scenarios/CreateLinuxVMSecureWebServer/create-linux-vm-secure-web-server.md b/scenarios/CreateLinuxVMSecureWebServer/create-linux-vm-secure-web-server.md new file mode 100644 index 000000000..4c62190b8 --- /dev/null +++ b/scenarios/CreateLinuxVMSecureWebServer/create-linux-vm-secure-web-server.md @@ -0,0 +1,837 @@ +--- +title: Create a NGINX Webserver Secured via HTTPS +description: This tutorial shows how to create a NGINX Webserver Secured via HTTPS. +author: mbifeld@microsoft.com +ms.topic: article +ms.date: 11/10/2023 +ms.custom: innovation-engine +--- + +# Create a NGINX Webserver Secured via HTTPS + +To secure web servers, a Transport Layer Security (TLS), previously known as Secure Sockets Layer (SSL), certificate can be used to encrypt web traffic. These TLS/SSL certificates can be stored in Azure Key Vault, and allow secure deployments of certificates to Linux virtual machines (VMs) in Azure. In this tutorial you learn how to: + +> [!div class="checklist"] + +> * Setup and secure Azure Networking +> * Create an Azure Key Vault +> * Generate or upload a certificate to the Key Vault +> * Create a VM and install the NGINX web server +> * Inject the certificate into the VM and configure NGINX with a TLS binding + +If you choose to install and use the CLI locally, this tutorial requires that you're running the Azure CLI version 2.0.30 or later. Run `az --version` to find the version. If you need to install or upgrade, see [Install Azure CLI]( https://learn.microsoft.com//cli/azure/install-azure-cli ). + +## Create a Resource Group + +Before you can create a secure Linux VM, create a resource group with az group create. The following example creates a resource group equal to the contents of the variable *MY_RESOURCE_GROUP_NAME* in the location specified by the variable contents *REGION*: + +```bash +export RANDOM_ID="$(openssl rand -hex 3)" +export MY_RESOURCE_GROUP_NAME="myResourceGroup$RANDOM_ID" +export REGION="centralindia" + +az group create \ + --name $MY_RESOURCE_GROUP_NAME \ + --location $REGION -o JSON +``` + +Results: + + +```JSON +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f", + "location": "centralindia", + "managedBy": null, + "name": "myResourceGroupb1404f", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Set up VM Network + +Use az network vnet create to create a virtual network named *$MY_VNET_NAME* with a subnet named *$MY_SN_NAME*in the *$MY_RESOURCE_GROUP_NAME*resource group. + +```bash +export NETWORK_PREFIX="$(($RANDOM % 254 + 1))" +export MY_VNET_NAME="myVNet$RANDOM_ID" +export MY_VNET_PREFIX="10.$NETWORK_PREFIX.0.0/16" +export MY_SN_NAME="mySN$RANDOM_ID" +export MY_SN_PREFIX="10.$NETWORK_PREFIX.0.0/24" + +az network vnet create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_VNET_NAME \ + --location $REGION \ + --address-prefix $MY_VNET_PREFIX \ + --subnet-name $MY_SN_NAME \ + --subnet-prefix $MY_SN_PREFIX -o JSON +``` + +Results: + + +```JSON +{ + "newVNet": { + "addressSpace": { + "addressPrefixes": [ + "10.168.0.0/16" + ] + }, + "bgpCommunities": null, + "ddosProtectionPlan": null, + "dhcpOptions": { + "dnsServers": [] + }, + "enableDdosProtection": false, + "enableVmProtection": null, + "encryption": null, + "extendedLocation": null, + "flowTimeoutInMinutes": null, + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.Network/virtualNetworks/myVNetb1404f", + "ipAllocations": null, + "location": "eastus", + "name": "myVNetb1404f", + "provisioningState": "Succeeded", + "resourceGroup": "myResourceGroupb1404f", + "subnets": [ + { + "addressPrefix": "10.168.0.0/24", + "addressPrefixes": null, + "applicationGatewayIpConfigurations": null, + "delegations": [], + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.Network/virtualNetworks/myVNetb1404f/subnets/mySNb1404f", + "ipAllocations": null, + "ipConfigurationProfiles": null, + "ipConfigurations": null, + "name": "mySNb1404f", + "natGateway": null, + "networkSecurityGroup": null, + "privateEndpointNetworkPolicies": "Disabled", + "privateEndpoints": null, + "privateLinkServiceNetworkPolicies": "Enabled", + "provisioningState": "Succeeded", + "purpose": null, + "resourceGroup": "myResourceGroupb1404f", + "resourceNavigationLinks": null, + "routeTable": null, + "serviceAssociationLinks": null, + "serviceEndpointPolicies": null, + "serviceEndpoints": null, + "type": "Microsoft.Network/virtualNetworks/subnets" + } + ], + "tags": {}, + "type": "Microsoft.Network/virtualNetworks", + "virtualNetworkPeerings": [] + } +} +``` + +Use az network public-ip create to create a standard zone-redundant public IPv4 address named *$MY_PUBLIC_IP_NAME* in *$MY_RESOURCE_GROUP_NAME*. + +```bash +export MY_PUBLIC_IP_NAME="myPublicIP$RANDOM_ID" +export MY_DNS_LABEL="mydnslabel$RANDOM_ID" + +az network public-ip create \ + --name $MY_PUBLIC_IP_NAME \ + --location $REGION \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --dns-name $MY_DNS_LABEL \ + --sku Standard \ + --allocation-method static \ + --version IPv4 \ + --zone 1 2 3 -o JSON +``` + +Results: + + +```JSON +{ + "publicIp": { + "ddosSettings": null, + "deleteOption": null, + "dnsSettings": { + "domainNameLabel": "mydnslabelb1404f", + "fqdn": "mydnslabelb1404f.eastus.cloudapp.azure.com", + "reverseFqdn": null + }, + "extendedLocation": null, + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.Network/publicIPAddresses/myPublicIPb1404f", + "idleTimeoutInMinutes": 4, + "ipAddress": "20.88.178.210", + "ipConfiguration": null, + "ipTags": [], + "linkedPublicIpAddress": null, + "location": "eastus", + "migrationPhase": null, + "name": "myPublicIPb1404f", + "natGateway": null, + "provisioningState": "Succeeded", + "publicIpAddressVersion": "IPv4", + "publicIpAllocationMethod": "Static", + "publicIpPrefix": null, + "resourceGroup": "myResourceGroupb1404f", + "servicePublicIpAddress": null, + "sku": { + "name": "Standard", + "tier": "Regional" + }, + "tags": null, + "type": "Microsoft.Network/publicIPAddresses", + "zones": [ + "1", + "2", + "3" + ] + } +} +``` + +Security rules in network security groups enable you to filter the type of network traffic that can flow in and out of virtual network subnets and network interfaces. To learn more about network security groups, see [Network security group overview](https://learn.microsoft.com/azure/virtual-network/network-security-groups-overview). + +```bash +export MY_NSG_NAME="myNSGName$RANDOM_ID" + +az network nsg create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_NSG_NAME \ + --location $REGION -o JSON +``` + +Results: + + +```JSON +{ + "NewNSG": { + "defaultSecurityRules": [ + { + "access": "Allow", + "description": "Allow inbound traffic from all VMs in VNET", + "destinationAddressPrefix": "VirtualNetwork", + "destinationAddressPrefixes": [], + "destinationPortRange": "*", + "destinationPortRanges": [], + "direction": "Inbound", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.Network/networkSecurityGroups/myNSGNameb1404f/defaultSecurityRules/AllowVnetInBound", + "name": "AllowVnetInBound", + "priority": 65000, + "protocol": "*", + "provisioningState": "Succeeded", + "resourceGroup": "myResourceGroupb1404f", + "sourceAddressPrefix": "VirtualNetwork", + "sourceAddressPrefixes": [], + "sourcePortRange": "*", + "sourcePortRanges": [], + "type": "Microsoft.Network/networkSecurityGroups/defaultSecurityRules" + } + ], + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.Network/networkSecurityGroups/myNSGNameb1404f", + "location": "eastus", + "name": "myNSGNameb1404f", + "provisioningState": "Succeeded", + "resourceGroup": "myResourceGroupb1404f", + "securityRules": [], + "type": "Microsoft.Network/networkSecurityGroups" + } +} +``` + +Open ports 22 (SSH), 80 (HTTP) and 443 (HTTPS) to allow SSH and Web traffic + +```bash +export MY_NSG_SSH_RULE="Allow-Access$RANDOM_ID" + +az network nsg rule create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --nsg-name $MY_NSG_NAME \ + --name $MY_NSG_SSH_RULE \ + --access Allow \ + --protocol Tcp \ + --direction Inbound \ + --priority 100 \ + --source-address-prefix '*' \ + --source-port-range '*' \ + --destination-address-prefix '*' \ + --destination-port-range 22 80 443 -o JSON +``` + +Results: + + +```JSON +{ + "access": "Allow", + "description": null, + "destinationAddressPrefix": "*", + "destinationAddressPrefixes": [], + "destinationApplicationSecurityGroups": null, + "destinationPortRange": null, + "destinationPortRanges": [ + "22", + "80", + "443" + ], + "direction": "Inbound", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.Network/networkSecurityGroups/myNSGNameb1404f/securityRules/MY_NSG_SSH_RULE", + "name": "MY_NSG_SSH_RULE", + "priority": 100, + "protocol": "Tcp", + "provisioningState": "Succeeded", + "resourceGroup": "myResourceGroupb1404f", + "sourceAddressPrefix": "*", + "sourceAddressPrefixes": [], + "sourceApplicationSecurityGroups": null, + "sourcePortRange": "*", + "sourcePortRanges": [], + "type": "Microsoft.Network/networkSecurityGroups/securityRules" +} +``` + +And finally create the Network Interface Card (NIC): + +```bash +export MY_VM_NIC_NAME="myVMNicName$RANDOM_ID" + +az network nic create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_VM_NIC_NAME \ + --location $REGION \ + --ip-forwarding false \ + --subnet $MY_SN_NAME \ + --vnet-name $MY_VNET_NAME \ + --network-security-group $MY_NSG_NAME \ + --public-ip-address $MY_PUBLIC_IP_NAME -o JSON +``` + +Results: + + +```JSON +{ + "NewNIC": { + "auxiliaryMode": "None", + "auxiliarySku": "None", + "disableTcpStateTracking": false, + "dnsSettings": { + "appliedDnsServers": [], + "dnsServers": [] + }, + "enableAcceleratedNetworking": false, + "enableIPForwarding": false, + "hostedWorkloads": [], + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.Network/networkInterfaces/myVMNicNameb1404f", + "ipConfigurations": [ + { + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.Network/networkInterfaces/myVMNicNameb1404f/ipConfigurations/ipconfig1", + "name": "ipconfig1", + "primary": true, + "privateIPAddress": "10.168.0.4", + "privateIPAddressVersion": "IPv4", + "privateIPAllocationMethod": "Dynamic", + "provisioningState": "Succeeded", + "resourceGroup": "myResourceGroupb1404f", + "subnet": { + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.Network/virtualNetworks/myVNetb1404f/subnets/mySNb1404f", + "resourceGroup": "myResourceGroupb1404f" + }, + "type": "Microsoft.Network/networkInterfaces/ipConfigurations" + } + ], + "location": "eastus", + "name": "myVMNicNameb1404f", + "networkSecurityGroup": { + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.Network/networkSecurityGroups/myNSGNameb1404f", + "resourceGroup": "myResourceGroupb1404f" + }, + "nicType": "Standard", + "provisioningState": "Succeeded", + "resourceGroup": "myResourceGroupb1404f", + "tapConfigurations": [], + "type": "Microsoft.Network/networkInterfaces", + "vnetEncryptionSupported": false + } +} +``` + +## Generate a certificate and store it in Azure Key Vault + +Azure Key Vault safeguards cryptographic keys and secrets, such as certificates or passwords. Key Vault helps streamline the certificate management process and enables you to maintain control of keys that access those certificates. You can create a self-signed certificate inside Key Vault, or upload an existing, trusted certificate that you already own. For this tutorial we'll create self-signed certificates inside the Key Vault and afterwards inject these certificates into a running VM. This process ensures that the most up-to-date certificates are installed on a web server during deployment. + +The following example creates an Azure Key Vault named *$MY_KEY_VAULT* in the chosen region *$REGION* with a retention policy of 7 days. This means once a secret, key, certificate, or key vault is deleted, it will remain recoverable for a configurable period of 7 to 90 calendar days. + +```bash +export MY_KEY_VAULT="mykeyvault$RANDOM_ID" + +az keyvault create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_KEY_VAULT \ + --location $REGION \ + --retention-days 7\ + --enabled-for-deployment true -o JSON +``` + +Results: + + +```JSON +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.KeyVault/vaults/myKeyVaultb1404f", + "location": "eastus", + "name": "myKeyVaultb1404f", + "properties": { + "accessPolicies": [ + { + "applicationId": null, + "permissions": { + "certificates": [ + "all" + ], + "keys": [ + "all" + ], + "secrets": [ + "all" + ], + "storage": [ + "all" + ] + }, + "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + } + ], + "createMode": null, + "enablePurgeProtection": null, + "enableRbacAuthorization": null, + "enableSoftDelete": true, + "enabledForDeployment": true, + "enabledForDiskEncryption": null, + "enabledForTemplateDeployment": null, + "hsmPoolResourceId": null, + "networkAcls": null, + "privateEndpointConnections": null, + "provisioningState": "Succeeded", + "publicNetworkAccess": "Enabled", + "sku": { + "family": "A", + "name": "standard" + }, + "softDeleteRetentionInDays": 7, + "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "vaultUri": "https://mykeyvaultb1404f.vault.azure.net/" + }, + "resourceGroup": "myResourceGroupb1404f", + "systemData": { + "createdAt": "2023-09-18T12:25:55.208000+00:00", + "createdBy": "example@microsoft.com", + "createdByType": "User", + "lastModifiedAt": "2023-09-18T12:25:55.208000+00:00", + "lastModifiedBy": "example@microsoft.com", + "lastModifiedByType": "User" + }, + "tags": {}, + "type": "Microsoft.KeyVault/vaults" +} +``` + +## Create a certificate and store in Azure key Vault + +Now let's generate a self-signed certificate with az keyvault certificate create that uses the default certificate policy: + +```bash +export MY_CERT_NAME="nginxcert$RANDOM_ID" + +az keyvault certificate create \ + --vault-name $MY_KEY_VAULT \ + --name $MY_CERT_NAME \ + --policy "$(az keyvault certificate get-default-policy)" -o JSON +``` + +Results: + + +```JSON +{ + "cancellationRequested": false, + "csr": "MIICr...", + "error": null, + "id": "https://mykeyvault67a7ba.vault.azure.net/certificates/nginxcert67a7ba/pending", + "issuerParameters": { + "certificateTransparency": null, + "certificateType": null, + "name": "Self" + }, + "name": "nginxcert67a7ba", + "status": "completed", + "statusDetails": null, + "target": "https://mykeyvault67a7ba.vault.azure.net/certificates/nginxcert67a7ba" +} +``` + +Finally, we need to prepare the certificate so it can be used during the VM create process. To do so we need to obtain the ID of the certificate with az keyvault secret list-versions, and convert the certificate with az vm secret format. The following example assigns the output of these commands to variables for ease of use in the next steps: + +```bash +export MY_VM_ID_NAME="myVMIDName$RANDOM_ID" + +az identity create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_VM_ID_NAME -o JSON +``` + +Results: + + +```JSON +{ + "clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroupb1404f/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myVMIDNameb1404f", + "location": "eastus", + "name": "myVMIDNameb1404f", + "principalId": "e09ebfce-97f0-4aff-9abd-415ebd6f915c", + "resourceGroup": "myResourceGroupb1404f", + "tags": {}, + "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "type": "Microsoft.ManagedIdentity/userAssignedIdentities" +} +``` + +```bash +MY_VM_PRINCIPALID=$(az identity show --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_ID_NAME --query principalId -o tsv) + +az keyvault set-policy \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_KEY_VAULT \ + --object-id $MY_VM_PRINCIPALID \ + --secret-permissions get list \ + --certificate-permissions get list -o JSON +``` + +Results: + + +```JSON +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupb1404f/providers/Microsoft.KeyVault/vaults/myKeyVaultb1404f", + "location": "eastus", + "name": "myKeyVaultb1404f", + "properties": { + "accessPolicies": [ + { + "applicationId": null, + "objectId": "ceeb4e98-5831-4d9f-b8ba-2ee14b3cdf80", + "permissions": { + "certificates": [ + "all" + ], + "keys": [ + "all" + ], + "secrets": [ + "all" + ], + "storage": [ + "all" + ] + }, + "tenantId": "bd7153ee-d085-4a28-a928-2f0ef402f076" + }, + { + "applicationId": null, + "objectId": "e09ebfce-97f0-4aff-9abd-415ebd6f915c", + "permissions": { + "certificates": [ + "list", + "get" + ], + "keys": null, + "secrets": [ + "list", + "get" + ], + "storage": null + }, + "tenantId": "bd7153ee-d085-4a28-a928-2f0ef402f076" + } + ], + "createMode": null, + "enablePurgeProtection": null, + "enableRbacAuthorization": null, + "enableSoftDelete": true, + "enabledForDeployment": true, + "enabledForDiskEncryption": null, + "enabledForTemplateDeployment": null, + "hsmPoolResourceId": null, + "networkAcls": null, + "privateEndpointConnections": null, + "provisioningState": "Succeeded", + "publicNetworkAccess": "Enabled", + "sku": { + "family": "A", + "name": "standard" + }, + "softDeleteRetentionInDays": 7, + "tenantId": "bd7153ee-d085-4a28-a928-2f0ef402f076", + "vaultUri": "https://mykeyvaultb1404f.vault.azure.net/" + }, + "resourceGroup": "myResourceGroupb1404f", + "systemData": { + "createdAt": "2023-09-18T12:25:55.208000+00:00", + "createdBy": "ajoian@microsoft.com", + "createdByType": "User", + "lastModifiedAt": "2023-09-18T12:48:08.966000+00:00", + "lastModifiedBy": "ajoian@microsoft.com", + "lastModifiedByType": "User" + }, + "tags": {}, + "type": "Microsoft.KeyVault/vaults" +} +``` + +## Create the VM + +Now create a VM with az vm create. Use the --custom-data parameter to pass in the cloud-init config file, named *cloud-init-nginx.txt*. +Cloud-init is a widely used approach to customize a Linux VM as it boots for the first time. You can use cloud-init to install packages and write files, or to configure users and security. As cloud-init runs during the initial boot process, there are no extra steps or required agents to apply your configuration. +When you create a VM, certificates and keys are stored in the protected /var/lib/waagent/ directory. In this example, we are installing and configuring the NGINX web server. + +```bash +export FQDN="${MY_DNS_LABEL}.${REGION}.cloudapp.azure.com" + +cat > cloud-init-nginx.txt </dev/null; echo "0 * * * * /root/convert_akv_cert.sh && service nginx reload") | crontab - + - service nginx restart +EOF +``` + +The following example creates a VM named *myVMName$UNIQUE_POSTFIX*: + +```bash +MY_VM_ID=$(az identity show --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_ID_NAME --query id -o tsv) +export MY_VM_NAME="myVMName$RANDOM_ID" +export MY_VM_IMAGE='Ubuntu2204' +export MY_VM_USERNAME="azureuser" +export MY_VM_SIZE='Standard_DS2_v2' + +az vm create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_VM_NAME \ + --image $MY_VM_IMAGE \ + --admin-username $MY_VM_USERNAME \ + --generate-ssh-keys \ + --assign-identity $MY_VM_ID \ + --size $MY_VM_SIZE \ + --custom-data cloud-init-nginx.txt \ + --nics $MY_VM_NIC_NAME +``` + +Results: + + +```JSON +{ + "fqdns": "mydnslabel67a7ba.eastus.cloudapp.azure.com", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup67a7ba/providers/Microsoft.Compute/virtualMachines/myVMName67a7ba", + "identity": { + "systemAssignedIdentity": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "userAssignedIdentities": { + "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroup67a7ba/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myVMIDName67a7ba": { + "clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "principalId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + } + } + }, + "location": "eastus", + "macAddress": "60-45-BD-D3-B5-29", + "powerState": "VM running", + "privateIpAddress": "10.56.0.4", + "publicIpAddress": "20.231.118.239", + "resourceGroup": "myResourceGroup67a7ba", + "zones": "" +} +``` + +## Deploying AKV extension for VM $vm_name to retrieve cert $cert_name from AKV $akv_name..." + +```bash +MY_CERT_ID=$(az keyvault certificate show --vault-name $MY_KEY_VAULT --name $MY_CERT_NAME --query sid -o tsv) +MY_VM_CLIENTID=$(az identity show --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_ID_NAME --query clientId -o tsv) +MY_AKV_EXT_SETTINGS="{\"secretsManagementSettings\":{\"pollingIntervalInS\":\"3600\",\"requireInitialSync\":"true",\"certificateStoreLocation\":\"/etc/nginx/ssl/\",\"observedCertificates\":[\"$MY_CERT_ID\"]},\"authenticationSettings\":{\"msiClientId\":\"${MY_VM_CLIENTID}\"}}" + +az vm extension set \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --vm-name $MY_VM_NAME \ + -n "KeyVaultForLinux" \ + --publisher Microsoft.Azure.KeyVault \ + --version 2.0 \ + --enable-auto-upgrade true \ + --settings $MY_AKV_EXT_SETTINGS -o JSON +``` + +Results: + + +```JSON +{ + "autoUpgradeMinorVersion": true, + "enableAutomaticUpgrade": true, + "forceUpdateTag": null, + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup67a7ba/providers/Microsoft.Compute/virtualMachines/myVMName67a7ba/extensions/KeyVaultForLinux", + "instanceView": null, + "location": "eastus", + "name": "KeyVaultForLinux", + "protectedSettings": null, + "protectedSettingsFromKeyVault": null, + "provisioningState": "Succeeded", + "publisher": "Microsoft.Azure.KeyVault", + "resourceGroup": "myResourceGroup67a7ba", + "settings": { + "secretsManagementSettings": { + "certificateStoreLocation": "/etc/nginx/ssl", + "observedCertificates": [ + "https://mykeyvault67a7ba.vault.azure.net/secrets/nginxcert67a7ba/aac9b30a90c04fc58bc230ae15b1148f" + ], + "pollingIntervalInS": "3600" + } + }, + "suppressFailures": null, + "tags": null, + "type": "Microsoft.Compute/virtualMachines/extensions", + "typeHandlerVersion": "2.0", + "typePropertiesType": "KeyVaultForLinux" +} +``` + +## Enable Azure AD login for a Linux Virtual Machine in Azure + +The following example deploys a VM and then installs the extension to enable Azure AD login for a Linux VM. VM extensions are small applications that provide post-deployment configuration and automation tasks on Azure virtual machines. + +```bash +az vm extension set \ + --publisher Microsoft.Azure.ActiveDirectory \ + --name AADSSHLoginForLinux \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --vm-name $MY_VM_NAME -o JSON +``` + +Results: + + +```JSON +{ + "autoUpgradeMinorVersion": true, + "enableAutomaticUpgrade": null, + "forceUpdateTag": null, + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupfa636b/providers/Microsoft.Compute/virtualMachines/myVMNamefa636b/extensions/AADSSHLoginForLinux", + "instanceView": null, + "location": "eastus", + "name": "AADSSHLoginForLinux", + "protectedSettings": null, + "protectedSettingsFromKeyVault": null, + "provisioningState": "Succeeded", + "publisher": "Microsoft.Azure.ActiveDirectory", + "resourceGroup": "myResourceGroupfa636b", + "settings": null, + "suppressFailures": null, + "tags": null, + "type": "Microsoft.Compute/virtualMachines/extensions", + "typeHandlerVersion": "1.0", + "typePropertiesType": "AADSSHLoginForLinux" +} +``` + +## Browse your secure website + +Validate that the application is running by visiting the application url: + +```bash +curl --max-time 120 -k "https://$FQDN" +``` + +Results: + + +```html + + + +Welcome to nginx! + + + +

Welcome to nginx!

+

If you see this page, the nginx web server is successfully installed and +working. Further configuration is required.

+ +

For online documentation and support please refer to +nginx.org.
+Commercial support is available at +nginx.com.

+ +

Thank you for using nginx.

+ + +``` \ No newline at end of file diff --git a/scenarios/CreateRHELVMAndSSH/create-rhel-vm-ssh.md b/scenarios/CreateRHELVMAndSSH/create-rhel-vm-ssh.md index 8efb38189..ac65901a3 100644 --- a/scenarios/CreateRHELVMAndSSH/create-rhel-vm-ssh.md +++ b/scenarios/CreateRHELVMAndSSH/create-rhel-vm-ssh.md @@ -28,19 +28,6 @@ To open the Cloud Shell, just select **Try it** from the upper right corner of a If you prefer to install and use the CLI locally, this quickstart requires Azure CLI version 2.0.30 or later. Run `az --version` to find the version. If you need to install or upgrade, see [Install Azure CLI]( /cli/azure/install-azure-cli). -## Define environment variables - -The first step is to define the environment variables. Environment variables are commonly used in Linux to centralize configuration data to improve consistency and maintainability of the system. Create the following environment variables to specify the names of resources that you create later in this tutorial: - -```bash -export RANDOM_ID="$(openssl rand -hex 3)" -export MY_RESOURCE_GROUP_NAME="myVMResourceGroup$RANDOM_ID" -export REGION="westeurope" -export MY_VM_NAME="myVM$RANDOM_ID" -export MY_USERNAME=azureuser -export MY_VM_IMAGE="RedHat:RHEL:8-LVM:latest" -``` - ## Log in to Azure using the CLI In order to run commands in Azure using the CLI, you need to log in first. Log in using the `az login` command. @@ -50,6 +37,9 @@ In order to run commands in Azure using the CLI, you need to log in first. Log i A resource group is a container for related resources. All resources must be placed in a resource group. The [az group create](/cli/azure/group) command creates a resource group with the previously defined $MY_RESOURCE_GROUP_NAME and $REGION parameters. ```bash +export RANDOM_ID="$(openssl rand -hex 3)" +export MY_RESOURCE_GROUP_NAME="myVMResourceGroup$RANDOM_ID" +export REGION="westeurope" az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION ``` @@ -79,6 +69,9 @@ The following example creates a VM and adds a user account. The `--generate-ssh- All other values are configured using environment variables. ```bash +export MY_VM_NAME="myVM$RANDOM_ID" +export MY_USERNAME=azureuser +export MY_VM_IMAGE="RedHat:RHEL:8-LVM:latest" az vm create \ --resource-group $MY_RESOURCE_GROUP_NAME \ --name $MY_VM_NAME \ diff --git a/scenarios/CreateSpeechService/create-speech-service.md b/scenarios/CreateSpeechService/create-speech-service.md new file mode 100644 index 000000000..08da60cd3 --- /dev/null +++ b/scenarios/CreateSpeechService/create-speech-service.md @@ -0,0 +1,198 @@ +--- +title: 'Quickstart: Create a Speech Services application on Azure' +description: Learn how to create a Speech Services application using Azure CLI. This will include creating a Speech service resource to support scenarios like speech-to-text and text-to-speech. +ms.topic: quickstart +ms.date: 10/07/2023 +author: azure-voice-guru +ms.author: azurevoice +ms.custom: cognitive-services, azure-cli, innovation-engine +--- + +# Quickstart: Create a Speech Services application on Azure + +In this quickstart, you will learn how to create a Speech Service resource using Azure CLI. This service enables scenarios such as speech-to-text, text-to-speech, and speech translation. + +--- + +## Prerequisites + +- Azure CLI installed and configured on your machine. +- Proper permissions to create resources in your Azure subscription. + +--- + +## Step 1: Create a Resource Group + +A resource group is a container that holds related resources for an Azure solution. + +```bash +export RANDOM_SUFFIX=$(openssl rand -hex 3) +export REGION="westus2" +export RESOURCE_GROUP_NAME="SpeechAppGroup$RANDOM_SUFFIX" +az group create --name $RESOURCE_GROUP_NAME --location $REGION --output json +``` + +### Results: + + + +```json +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/SpeechAppGroupxxx", + "location": "westus2", + "managedBy": null, + "name": "SpeechAppGroupxxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +--- + +## Step 2: Create a Speech Service Resource + +The Speech Service is part of Azure Cognitive Services and provides functionalities like speech-to-text, text-to-speech, and translation. You will create this resource within the resource group. + +```bash +export SPEECH_SERVICE_NAME="MySpeechService$RANDOM_SUFFIX" +az cognitiveservices account create \ + --name $SPEECH_SERVICE_NAME \ + --resource-group $RESOURCE_GROUP_NAME \ + --kind SpeechServices \ + --sku S0 \ + --location $REGION \ + --yes \ + --output json +``` + +### Results: + + + +```json +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/SpeechAppGroupxxx/providers/Microsoft.CognitiveServices/accounts/MySpeechServicexxx", + "location": "westus2", + "name": "MySpeechServicexxx", + "properties": { + "provisioningState": "Succeeded" + }, + "sku": { + "name": "S0" + }, + "type": "Microsoft.CognitiveServices/accounts" +} +``` + +--- + +## Step 3: Ensure Resource Provisioning Completes + +Ensure the Speech Service resource is fully provisioned before proceeding. A polling mechanism is implemented here to verify the provisioning state. + +--- + +### Updated Polling with JSON Validation + +```bash +export PROVISIONING_STATE=$(az cognitiveservices account show \ + --only-show-errors \ + --name $SPEECH_SERVICE_NAME \ + --resource-group $RESOURCE_GROUP_NAME \ + --query "properties.provisioningState" -o tsv 2>/dev/null || echo "Unknown") +echo "Current provisioning state: $PROVISIONING_STATE" +``` + +### Results: + + + +```text +Current provisioning state: Succeeded +``` + +--- + +## Step 4: Retrieve Keys and Endpoint + +You will need the keys and endpoint to use the Speech Service in your applications. + +--- + +### Retrieve Keys + +Fetch the keys for accessing the Speech Service. + +```bash +KEYS_JSON=$(az cognitiveservices account keys list \ + --only-show-errors \ + --name $SPEECH_SERVICE_NAME \ + --resource-group $RESOURCE_GROUP_NAME \ + -o json 2>/dev/null) + +if [ -z "$KEYS_JSON" ] || [ "$KEYS_JSON" == "null" ]; then + echo "Error: Failed to retrieve keys. Verify the resource status in the Azure portal." + exit 1 +fi + +export KEY1=$(echo "$KEYS_JSON" | jq -r '.key1') +export KEY2=$(echo "$KEYS_JSON" | jq -r '.key2') + +if [ -z "$KEY1" ] || [ "$KEY2" == "null" ]; then + echo "Error: Retrieved keys are empty or invalid. Inspect the resource settings." + exit 1 +fi + +echo "Key1: Retrieved successfully" +echo "Key2: Retrieved successfully" +``` + +### Results: + + + +```output +Key1: Retrieved successfully +Key2: Retrieved successfully +``` + +--- + +### Retrieve Endpoint + +Fetch the endpoint for the Speech Service. + +--- + +### Updated Endpoint Retrieval + +```bash +ENDPOINT_JSON=$(az cognitiveservices account show \ + --name $SPEECH_SERVICE_NAME \ + --resource-group $RESOURCE_GROUP_NAME \ + -o json 2>/dev/null) + +if echo "$ENDPOINT_JSON" | grep -q '"code": "404"'; then + echo "Error: Resource not found. Verify the resource name, group, or region." + exit 1 +fi + +export ENDPOINT=$(echo "$ENDPOINT_JSON" | jq -r '.properties.endpoint') +if [ -z "$ENDPOINT" ] || [ "$ENDPOINT" == "null" ]; then + echo "Error: Failed to retrieve endpoint. Verify the resource status in the Azure portal." + exit 1 +fi + +echo "Endpoint: $ENDPOINT" +``` + +### Results: + + + +```text +https://xxxxxxxxxxxxxxxxxxxxx.cognitiveservices.azure.com/ +``` diff --git a/scenarios/DeployCassandraOnAKS/deploy-cassandra-on-aks.md b/scenarios/DeployCassandraOnAKS/deploy-cassandra-on-aks.md new file mode 100644 index 000000000..9e0cab122 --- /dev/null +++ b/scenarios/DeployCassandraOnAKS/deploy-cassandra-on-aks.md @@ -0,0 +1,259 @@ +--- +title: "Deploy a Cassandra Cluster on AKS" +description: Learn how to deploy a Cassandra cluster on an Azure Kubernetes Service (AKS) cluster using Azure CLI and Kubernetes manifests. +ms.topic: tutorial +ms.date: 10/12/2023 +author: execdocwriter +ms.author: execdocwriter +ms.custom: aks, cassandra, azurecli, kubernetes, innovation-engine +--- + +# Deploy a Cassandra Cluster on AKS + +In this tutorial, you'll deploy an open-source Apache Cassandra cluster on Azure Kubernetes Service (AKS) and manage it using Kubernetes. This tutorial demonstrates creating an AKS cluster, deploying Cassandra, and verifying the deployment. + +## Prerequisites + +1. Install Azure CLI. You can follow [Install the Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli) for instructions. +2. Install `kubectl`. You can use the `az aks install-cli` command to install it if you are using Azure Cloud Shell. + + +## Step 1: Create a Resource Group + +Create an Azure resource group to contain the AKS cluster and other resources. + +```bash +export RANDOM_SUFFIX="$(openssl rand -hex 3)" +export REGION="westus2" +export MY_RESOURCE_GROUP_NAME="MyAKSResourceGroup$RANDOM_SUFFIX" + +# Create a resource group in the specified region +az group create \ + --name $MY_RESOURCE_GROUP_NAME \ + --location $REGION +``` + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/CassandraClusterRGxxx", + "location": "centralindia", + "managedBy": null, + "name": "CassandraClusterRGxxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Step 2: Create an AKS Cluster + +Now, create an AKS cluster in the resource group. + +```bash +export MY_AKS_CLUSTER_NAME="MyAKSCluster$RANDOM_SUFFIX" + +# Create the AKS cluster +az aks create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_AKS_CLUSTER_NAME \ + --node-count 3 \ + --enable-addons monitoring \ + --generate-ssh-keys +``` + +## Step 3: Connect to the AKS Cluster + +Retrieve the AKS cluster credentials and configure `kubectl`. + +```bash +az aks get-credentials \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_AKS_CLUSTER_NAME +``` + +After running the command, your `kubectl` context will be set to the newly created AKS cluster. Verify the connection: + +```bash +kubectl get nodes +``` + +Results: + + + +```text +NAME STATUS ROLES AGE VERSION +aks-nodepool1-xxxxx-vmss000000 Ready agent 3m56s v1.26.0 +aks-nodepool1-xxxxx-vmss000001 Ready agent 3m52s v1.26.0 +aks-nodepool1-xxxxx-vmss000002 Ready agent 3m48s v1.26.0 +``` + +## Step 4: Deploy the Cassandra Cluster + +Create a Kubernetes manifest file in Cloud Shell to define the Cassandra deployment. Use a name like `cassandra-deployment.yaml`. + +```bash +cat < cassandra-deployment.yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: cassandra +spec: + selector: + matchLabels: + app: cassandra + serviceName: "cassandra" + replicas: 3 + template: + metadata: + labels: + app: cassandra + spec: + containers: + - name: cassandra + image: cassandra:latest + ports: + - containerPort: 9042 + name: cql + volumeMounts: + - mountPath: /var/lib/cassandra + name: cassandra-data + volumes: + - name: cassandra-data +EOF + +# Apply the manifest to the cluster +kubectl apply -f cassandra-deployment.yaml +``` + +Results: + + + +```text +statefulset.apps/cassandra created +``` + +## Step 5: Create a Headless Service for Cassandra + +Create a Kubernetes manifest file in Cloud Shell to define the Cassandra headless service. Use a name like `cassandra-service.yaml`. + +```bash +cat < cassandra-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: cassandra + namespace: default +spec: + clusterIP: None + selector: + app: cassandra + ports: + - name: cql + port: 9042 + targetPort: 9042 +EOF + +# Apply the service manifest to the cluster +kubectl apply -f cassandra-service.yaml +``` + +## Step 6: Verify Cassandra Deployment + +Check the status of the Cassandra pods to ensure deployment is successful. + +```bash +while true; do + POD_STATUSES=$(kubectl get pods -l app=cassandra -o jsonpath='{.items[*].status.phase}') + ALL_RUNNING=true + for STATUS in $POD_STATUSES; do + if [ "$STATUS" != "Running" ]; then + ALL_RUNNING=false + break + fi + done + + if [ "$ALL_RUNNING" = true ]; then + kubectl get pods -l app=cassandra + break + else + sleep 10 + fi +done +``` + +Results: + + + +```text +NAME READY STATUS RESTARTS AGE +cassandra-0 1/1 Running 0 3m +cassandra-1 1/1 Running 0 2m +cassandra-2 1/1 Running 0 1m +``` + +Verify the Cassandra StatefulSet. + +```bash +kubectl get statefulset cassandra +``` + +Results: + + + +```text +NAME READY AGE +cassandra 3/3 3m +``` + +## Step 7: Access Cassandra Cluster + +Create a temporary Pod to access the Cassandra cluster using `cqlsh`, the Cassandra query tool. + +```bash +kubectl run cassandra-client --rm -it --image=cassandra:latest -- /bin/bash +``` + +Once you are inside the Pod, connect to the Cassandra cluster using `cqlsh`. + +```bash +for i in {1..10}; do + echo "Attempt $i: Trying to connect to Cassandra cluster..." + # Try to run a simple cqlsh command (e.g. list keyspaces) + cql_output=$(cqlsh cassandra-0.cassandra -e "DESC KEYSPACES;" 2>&1) + if echo "$cql_output" | grep -q "system"; then + echo "Connected to Cassandra." + break + else + echo "cqlsh not ready yet. Retrying in 10 seconds..." + sleep 10 + fi +done +``` + +You should now be connected to the Cassandra database. + +> **Note:** When you're done testing, exit the shell and delete the Pod automatically. + +Results: + + + +```text +Connected to Test Cluster at cassandra-0.cassandra:9042. +[cqlsh 5.0.1 | Cassandra 4.0.0 | CQL spec 3.4.0 | Native protocol v4] +Use HELP for help. +``` + +This tutorial deployed an Apache Cassandra cluster on AKS. You managed the cluster using Kubernetes manifests and verified its deployment. + +> **IMPORTANT:** Do not forget to clean up unnecessary resources like the AKS cluster if you no longer need them. \ No newline at end of file diff --git a/scenarios/DeployClickhouseOnAKS/deploy-clickhouse-on-aks.md b/scenarios/DeployClickhouseOnAKS/deploy-clickhouse-on-aks.md new file mode 100644 index 000000000..7240a0af3 --- /dev/null +++ b/scenarios/DeployClickhouseOnAKS/deploy-clickhouse-on-aks.md @@ -0,0 +1,197 @@ +--- +title: 'Deploy ClickHouse Cluster on AKS' +description: Learn how to deploy a ClickHouse Cluster on Azure Kubernetes Service (AKS) using Azure CLI and Kubernetes manifests. +ms.topic: quickstart +ms.date: 10/05/2023 +author: azure-execdocwriter +ms.author: azureexecdocwriter +ms.custom: devx-track-azurecli, mode-api, innovation-engine, aks-related-content +--- + +# Deploy ClickHouse Cluster on AKS + +This Exec Doc demonstrates how to deploy a ClickHouse Cluster on Azure Kubernetes Service (AKS). ClickHouse is an open-source column-oriented database management system. By following this guide, you'll create an AKS cluster, deploy a ClickHouse cluster on it using a Kubernetes manifest, and verify the deployment. + +## Prerequisites + +Ensure that you have the following: + +1. An Azure subscription. +2. The Azure CLI installed (v2.30.0 or later). +3. Access to `kubectl` CLI to manage your Kubernetes cluster. +4. Azure CLI extensions enabled for AKS (`az extension add --name aks`). + +## Step 1: Create a Resource Group + +Create a new Azure resource group to contain all resources related to the deployment. + +```bash +export RANDOM_SUFFIX="$(openssl rand -hex 3)" +export REGION="westus2" +export MY_RESOURCE_GROUP="MyAKSResourceGroup$RANDOM_SUFFIX" +az group create --name $MY_RESOURCE_GROUP --location $REGION +``` + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxx-xxxxx-xxxxx-xxxxx/resourceGroups/MyAKSResourceGroupxxx", + "location": "centralindia", + "managedBy": null, + "name": "MyAKSResourceGroupxxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Step 2: Create an AKS Cluster + +Create an Azure Kubernetes Service (AKS) cluster in the resource group. + +```bash +export MY_AKS_CLUSTER="MyAKSCluster$RANDOM_SUFFIX" +az aks create --resource-group $MY_RESOURCE_GROUP --name $MY_AKS_CLUSTER --node-count 3 --generate-ssh-keys +``` + +## Step 3: Connect to the AKS Cluster + +Obtain the Kubernetes credentials to connect to your AKS cluster. + +```bash +az aks get-credentials --resource-group $MY_RESOURCE_GROUP --name $MY_AKS_CLUSTER +``` + +Results: + + + +```text +Merged "MyAKSClusterxxx" as current context in /home/user/.kube/config +``` + +## Step 4: Create a Namespace for ClickHouse + +Create a Kubernetes namespace to host the ClickHouse deployment. + +```bash +kubectl create namespace clickhouse +``` + +Results: + + + +```text +namespace/clickhouse created +``` + +## Step 5: Deploy ClickHouse on AKS + +Use the following Kubernetes manifest to deploy ClickHouse. Save this manifest into a file named **clickhouse-deployment.yaml**. + +```bash +cat < clickhouse-deployment.yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: clickhouse + namespace: clickhouse +spec: + serviceName: "clickhouse" + replicas: 3 + selector: + matchLabels: + app: clickhouse + template: + metadata: + labels: + app: clickhouse + spec: + containers: + - name: clickhouse + image: yandex/clickhouse-server:latest + resources: + requests: + cpu: "500m" + memory: "512Mi" + limits: + cpu: "1" + memory: "1Gi" + ports: + - containerPort: 8123 + name: http + - containerPort: 9000 + name: native + volumeMounts: + - name: clickhouse-data + mountPath: /var/lib/clickhouse + volumeClaimTemplates: + - metadata: + name: clickhouse-data + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 10Gi +EOF +``` + +Apply the configuration to deploy ClickHouse. + +```bash +kubectl apply -f clickhouse-deployment.yaml +``` + +Results: + + + +```text +statefulset.apps/clickhouse created +persistentvolumeclaim/clickhouse-pvc created +``` + +## Step 6: Verify the Deployment + +Check if the ClickHouse pods are running correctly: + +```bash +while true; do + POD_STATUSES=$(kubectl get pods -n clickhouse -o jsonpath='{.items[*].status.phase}') + ALL_RUNNING=true + for STATUS in $POD_STATUSES; do + if [ "$STATUS" != "Running" ]; then + ALL_RUNNING=false + break + fi + done + + if [ "$ALL_RUNNING" = true ]; then + kubectl get pods -n clickhouse + break + else + sleep 10 + fi +done +``` + +Results: + + + +```text +NAME READY STATUS RESTARTS AGE +clickhouse-0 1/1 Running 0 2m +clickhouse-1 1/1 Running 0 2m +clickhouse-2 1/1 Running 0 2m +``` + +## Summary + +You have successfully deployed a ClickHouse cluster on AKS. You can now connect to the ClickHouse service using the appropriate service endpoint or Kubernetes port forwarding. \ No newline at end of file diff --git a/scenarios/DeployHAPGOnAKSTerraform/app-deployment.yaml b/scenarios/DeployHAPGOnAKSTerraform/app-deployment.yaml new file mode 100644 index 000000000..2a9dbf000 --- /dev/null +++ b/scenarios/DeployHAPGOnAKSTerraform/app-deployment.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pg-app +spec: + replicas: 2 + selector: + matchLabels: + app: pg-app + template: + metadata: + labels: + app: pg-app + spec: + containers: + - name: pg-app + image: postgres:11 + env: + - name: POSTGRES_DB + value: \ No newline at end of file diff --git a/scenarios/DeployHAPGOnAKSTerraform/app-service.yaml b/scenarios/DeployHAPGOnAKSTerraform/app-service.yaml new file mode 100644 index 000000000..5b4dbe06d --- /dev/null +++ b/scenarios/DeployHAPGOnAKSTerraform/app-service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: pg-app-service +spec: + type: LoadBalancer + ports: + - port: 5432 + targetPort: 5432 + selector: + app: pg-app \ No newline at end of file diff --git a/scenarios/DeployHAPGOnAKSTerraform/deploy-ha-pg-on-aks-terraform.md b/scenarios/DeployHAPGOnAKSTerraform/deploy-ha-pg-on-aks-terraform.md new file mode 100644 index 000000000..da31384e5 --- /dev/null +++ b/scenarios/DeployHAPGOnAKSTerraform/deploy-ha-pg-on-aks-terraform.md @@ -0,0 +1,403 @@ +--- +title: Create a Highly Available PostgreSQL Cluster on Azure Kubernetes Service (AKS) using Terraform +description: This tutorial shows how to create a Highly Available PostgreSQL cluster on AKS using the CloudNativePG operator +author: russd2357,kenkilty +ms.author: rdepina,kenkilty +ms.topic: article +ms.date: 06/26/2024 +ms.custom: innovation-engine, linux-related content +--- +# Create a Highly Available PostgreSQL Cluster on Azure Kubernetes Service (AKS) using Terraform. + +In this guide, you will deploy a highly-available PostgreSQL cluster that spans multiple Azure availability zones. You will walk through the steps required to set up the PostgreSQL cluster running on [Azure Kubernetes Service](https://learn.microsoft.com/en-us/azure/aks/what-is-aks) (AKS) and perform basic Postgres operations such as backup and restore. + + +## Installing Terraform + +3. Download Terraform +Use wget to download the latest version of Terraform. You can find the latest version on the Terraform releases page. For example, to download version 1.5.0: + +```bash +if ! command -v terraform &> /dev/null +then + wget https://releases.hashicorp.com/terraform/1.5.0/terraform_1.5.0_linux_amd64.zip +fi +``` + +4. Unzip the Downloaded File +After downloading, you need to extract the Terraform binary from the zip file: + +```bash +if ! command -v terraform &> /dev/null +then + unzip terraform_1.5.0_linux_amd64.zip +fi +``` + + +5. Move Teffaform to a Directory in Your PATH +To make Terraform accessible from anywhere in your terminal, move it to /usr/local/bin: + +```bash +if ! command -v terraform &> /dev/null +then + # Create a bin directory in your home directory if it doesn't exist + mkdir -p $HOME/bin + + # Move Terraform to the bin directory in your home directory + mv terraform $HOME/bin/ + + # Add the bin directory to your PATH if it's not already included + if [[ ":$PATH:" != *":$HOME/bin:"* ]]; then + export PATH="$HOME/bin:$PATH" + fi +fi +``` + + +6. Verify the Installation +Finally, check if Terraform is installed correctly by checking its version: + +```bash +terraform -v +``` + +Results: + +```output +Terraform v1.5.0 +``` + + +## Creating a Highly Available PostgreSQL Cluster on Azure Kubernetes Service (AKS) Using Terraform + +1. Create a Terraform Configuration File Create a file named main.tf with the following content: + +```bash +# Generate a random suffix +export RANDOM_SUFFIX=$(openssl rand -hex 4) +export RESOURCE_GROUP_NAME="pg-ha-rg$RANDOM_SUFFIX" +export AKS_CLUSTER_NAME="pg-ha-aks$RANDOM_SUFFIX" +export POSTGRES_SERVER_NAME="pg-ha-server$RANDOM_SUFFIX" +export POSTGRES_DATABASE_NAME=$POSTGRES_DATABASE_NAME +export POSTGRES_DATABASE_PASSWORD=$(openssl rand -base64 32) +export POSTGRES_DATABASE_USER="pgadmin$RANDOM_SUFFIX" + +# Get the subscription ID programmatically +export TF_VAR_subscription_id=$(az account show --query id --output tsv) + +# Set additional environment variables for Terraform +export TF_VAR_resource_group_name=$RESOURCE_GROUP_NAME +export TF_VAR_location="East US" +export TF_VAR_aks_cluster_name=$AKS_CLUSTER_NAME +export TF_VAR_postgres_server_name=$POSTGRES_SERVER_NAME +export TF_VAR_postgres_database_name=$POSTGRES_DATABASE_NAME +export TF_VAR_postgres_database_user=$POSTGRES_DATABASE_USER +export TF_VAR_postgres_database_password=$POSTGRES_DATABASE_PASSWORD +``` + +```text +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "rg" { + name = $RESOURCE_GROUP_NAME + location = "West Europe" +} + +resource "azurerm_kubernetes_cluster" "aks" { + name = $AKS_CLUSTER_NAME + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + dns_prefix = "pgha" + + agent_pool_profile { + name = "agentpool" + count = 3 + vm_size = "Standard_DS2_v2" # SKU for AKS + os_type = "Linux" + mode = "System" + } + + identity { + type = "SystemAssigned" + } + + role_based_access_control { + enabled = true + } +} + +resource "azurerm_postgresql_server" "pg_server" { + name = $POSTGRES_SERVER_NAME + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + version = "11" + sku { + name = "B_Gen5_2" # SKU for PostgreSQL + tier = "Basic" + capacity = 2 + } + storage_profile { + storage_mb = 5120 + } + administrator_login = $POSTGRES_DATABASE_USER + administrator_login_password = $POSTGRES_DATABASE_PASSWORD + ssl_enforcement_enabled = true +} + +resource "azurerm_postgresql_database" "pg_database" { + name = $POSTGRES_DATABASE_NAME + resource_group_name = azurerm_resource_group.rg.name + server_name = azurerm_postgresql_server.pg_server.name + charset = "UTF8" + collation = "English_United States.1252" +} +``` + + +2. Initialize Terraform Run the following command to initialize your Terraform configuration: + +```bash +terraform init +``` + +Results: + +```output +Initializing the backend... + +Initializing provider plugins... +- Finding hashicorp/azurerm versions matching ">= 2.0.0"... +- Installing hashicorp/azurerm v2.0.0... +- Installed hashicorp/azurerm v2.0.0 (signed by HashiCorp) + +Terraform has been successfully initialized! +``` + + +3. Validate the Configuration Check if your configuration is valid: + +```bash +terraform validate +``` + +Results: + +```output +Success! The configuration is valid. +``` + + +4. Plan the Deployment Generate an execution plan: + +```bash +terraform plan +``` + +Results: + +```output +Terraform will perform the following actions: + + # azurerm_kubernetes_cluster.aks will be created + + resource "azurerm_kubernetes_cluster" "aks" { + ... + } + + # azurerm_postgresql_server.pg_server will be created + + resource "azurerm_postgresql_server" "pg_server" { + ... + } + +Plan: 3 to add, 0 to change, 0 to destroy. +``` + + +5. Apply the Configuration Deploy the resources: + +```bash +terraform apply -auto-approve +``` + +Results: + +```output +azurerm_resource_group.rg: Creating... +azurerm_resource_group.rg: Creation complete after 5s [id=/subscriptions/.../resourceGroups/pg-ha-rg] +azurerm_kubernetes_cluster.aks: Creating... +azurerm_postgresql_server.pg_server: Creating... +... +Apply complete! Resources: 3 added, 0 changed, 0 destroyed. +``` + + +6. Verify the Deployment Check the status of the AKS cluster: + +```bash +az aks show --resource-group $RESOURCE_GROUP_NAME --name $AKS_CLUSTER_NAME --output table +``` + +Results: + +```output +Name ResourceGroup Location KubernetesVersion ProvisioningState +----------- --------------- ----------- -------------------- ------------------- +pg-ha-aks pg-ha-rg West Europe 1.20.7 Succeeded +``` + + +7. Connect to PostgreSQL To connect to your PostgreSQL server, you can use the following command: + +```bash +psql "host=$POSTGRES_SERVER_NAME.postgres.database.azure.com dbname=$POSTGRES_DATABASE_NAME user=$POSTGRES_DATABASE_USER@$POSTGRES_SERVER_NAME password=$POSTGRES_DATABASE_PASSWORD sslmode=require" +``` + +Results: + +```output +psql (12.3) +Type "help" for help. + +mydatabase=# +``` + +8. Deploy a Sample Application To test the PostgreSQL setup, you can deploy a simple application. Create a file named app-deployment.yaml with the following content: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pg-app +spec: + replicas: 2 + selector: + matchLabels: + app: pg-app + template: + metadata: + labels: + app: pg-app + spec: + containers: + - name: pg-app + image: postgres:11 + env: + - name: POSTGRES_DB + value: +``` + +## Steps to Test Application +1. Expose the Application First, you need to create a service to expose your application. Create a file named app-service.yaml with the following content: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: pg-app-service +spec: + type: LoadBalancer + ports: + - port: 5432 + targetPort: 5432 + selector: + app: pg-app +``` + +Apply this configuration to your AKS cluster: + +```bash +kubectl apply -f app-service.yaml +``` + +Results: + +```output +service/pg-app-service created +``` + +2. Check the Status of the Service After exposing the application, check the status of the service to get the external IP address: + +```bash +kubectl get services +``` + +Results: + +```output +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +pg-app-service LoadBalancer 10.0.0.1 5432:XXXXX/TCP 1m +``` + +Wait a few moments until the EXTERNAL-IP is assigned. It may take a couple of minutes. + + +3. Connect to the Application Once the external IP is assigned, you can connect to the PostgreSQL database using the following command. Replace with the actual external IP address you obtained from the previous step: + +```bash +# Fetch the external IP address +export EXTERNAL_IP=$(kubectl get services pg-app-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + +# Check if the EXTERNAL_IP is not empty +if [ -z "$EXTERNAL_IP" ]; then + echo "Error: External IP address not found. Please wait a few moments and try again." + exit 1 +fi + +# Connect to the PostgreSQL database +psql "host=$EXTERNAL_IP dbname=mydatabase user=pgadmin@pg-ha-server password=YourPassword123! sslmode=require" +``` + +Results: + +```output +psql (12.3) +Type "help" for help. + +mydatabase=# +``` + +4. Clean Up Resources +When done, destroy the resources: + +```bash +terraform destroy -auto-approve +``` + +Results: + +```output +Results: + +```output +psql (12.3) +Type "help" for help. + +mydatabase=# +``` + + + +To learn more about AKS and walk through a complete code-to-deployment example, continue to the Kubernetes cluster tutorial. + +> [!div class="nextstepaction"] +> [AKS tutorial][aks-tutorial] + + +[kubectl]: https://kubernetes.io/docs/reference/kubectl/ +[kubectl-apply]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#apply +[kubectl-get]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#get + + +[kubernetes-concepts]: ../concepts-clusters-workloads.md +[aks-tutorial]: ../tutorial-kubernetes-prepare-app.md +[azure-resource-group]: ../../azure-resource-manager/management/overview.md +[az-aks-create]: /cli/azure/aks#az-aks-create +[az-aks-get-credentials]: /cli/azure/aks#az-aks-get-credentials +[az-aks-install-cli]: /cli/azure/aks#az-aks-install-cli +[az-group-create]: /cli/azure/group#az-group-create +[az-group-delete]: /cli/azure/group#az-group-delete +[kubernetes-deployment]: ../concepts-clusters-workloads.md#deployments-and-yaml-manifests +[aks-solution-guidance]: /azure/architecture/reference-architectures/containers/aks-start-here?toc=/azure/aks/toc.json&bc=/azure/aks/breadcrumb/toc.json +[baseline-reference-architecture]: /azure/architecture/reference-architectures/containers/aks/baseline-aks?toc=/azure/aks/toc.json&bc=/azure/aks/breadcrumb/toc.json \ No newline at end of file diff --git a/scenarios/DeployHAPGOnAKSTerraform/main.tf b/scenarios/DeployHAPGOnAKSTerraform/main.tf new file mode 100644 index 000000000..77cdd04e4 --- /dev/null +++ b/scenarios/DeployHAPGOnAKSTerraform/main.tf @@ -0,0 +1,46 @@ +provider "azurerm" { + features {} + subscription_id = var.subscription_id +} + +resource "azurerm_resource_group" "rg" { + name = var.resource_group_name + location = var.location +} + +resource "azurerm_kubernetes_cluster" "aks" { + name = var.aks_cluster_name + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + dns_prefix = "pgha" + + default_node_pool { + name = "agentpool" + node_count = 3 + vm_size = "Standard_DS2_v2" + } + + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_postgresql_server" "pg_server" { + name = var.postgres_server_name + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + version = "11" + administrator_login = var.postgres_database_user + administrator_login_password = var.postgres_database_password + ssl_enforcement_enabled = true + sku_name = "B_Gen5_2" + storage_mb = 5120 +} + +resource "azurerm_postgresql_database" "pg_database" { + name = var.postgres_database_name + resource_group_name = azurerm_resource_group.rg.name + server_name = azurerm_postgresql_server.pg_server.name + charset = "UTF8" + collation = "English_United States.1252" +} \ No newline at end of file diff --git a/scenarios/DeployHAPGOnAKSTerraform/variables.tf b/scenarios/DeployHAPGOnAKSTerraform/variables.tf new file mode 100644 index 000000000..cbfce95d9 --- /dev/null +++ b/scenarios/DeployHAPGOnAKSTerraform/variables.tf @@ -0,0 +1,40 @@ +variable "subscription_id" { + description = "Azure Subscription ID" + type = string +} + +variable "resource_group_name" { + description = "Resource Group Name" + type = string +} + +variable "location" { + description = "Azure Region" + type = string +} + +variable "aks_cluster_name" { + description = "AKS Cluster Name" + type = string +} + +variable "postgres_server_name" { + description = "PostgreSQL Server Name" + type = string +} + +variable "postgres_database_name" { + description = "PostgreSQL Database Name" + type = string +} + +variable "postgres_database_user" { + description = "PostgreSQL Database User" + type = string +} + +variable "postgres_database_password" { + description = "PostgreSQL Database Password" + type = string + sensitive = true +} \ No newline at end of file diff --git a/scenarios/DeployHAPGOnARO/deploy-ha-pg-on-aro.md b/scenarios/DeployHAPGOnARO/deploy-ha-pg-on-aro.md new file mode 100644 index 000000000..572fc5dec --- /dev/null +++ b/scenarios/DeployHAPGOnARO/deploy-ha-pg-on-aro.md @@ -0,0 +1,506 @@ +--- +title: Create a Highly Available PostgreSQL Cluster on Azure Red Hat OpenShift +description: This tutorial shows how to create a Highly Available PostgreSQL cluster on Azure Red Hat OpenShift (ARO) using the CloudNativePG operator +author: russd2357 +ms.author: rdepina +ms.topic: article +ms.date: 04/30/2024 +ms.custom: innovation-engine, linux-related content +--- + +# Create a Highly Available PostgreSQL Cluster on Azure Red Hat OpenShift + +## Login to Azure using the CLI + +In order to run commands against Azure using the CLI you need to login. This is done, very simply, though the `az login` command: + +## Check for Prerequisites + +Next, check for prerequisites. This can be done by running the following commands: + +- RedHat OpenShift: `az provider register -n Microsoft.RedHatOpenShift --wait` +- kubectl: `az aks install-cli` +- Openshift Client: `mkdir ~/ocp ; wget -q https://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest/openshift-client-linux.tar.gz -O ~/ocp/openshift-client-linux.tar.gz ; tar -xf ~/ocp/openshift-client-linux.tar.gz ; export PATH="$PATH:~/ocp"` + +## Create a resource group + +A resource group is a container for related resources. All resources must be placed in a resource group. We will create one for this tutorial. The following command creates a resource group with the previously defined $RG_NAME, $LOCATION, and $RGTAGS parameters. + +```bash +export RGTAGS="owner=ARO Demo" +export LOCATION="westus" +export LOCAL_NAME="arodemo" +export RG_NAME="rg-arodemo-perm" +``` + +## Create VNet + +In this section, you'll be creating a Virtual Network (VNet) in Azure. Start by defining several environment variables. These variables will hold the names of your VNet and subnets, as well as the CIDR block for your VNet. Next, create the VNet with the specified name and CIDR block in your resource group using the az network vnet create command. This process may take a few minutes. + +```bash +export VNET_NAME="vnet-${LOCAL_NAME}" +export SUBNET1_NAME="sn-main" +export SUBNET2_NAME="sn-worker" +export VNET_CIDR="10.0.0.0/22" +az network vnet create -g $RG_NAME -n $VNET_NAME --address-prefixes $VNET_CIDR +``` + +Results: + + +```json +{ + "newVNet": { + "addressSpace": { + "addressPrefixes": [ + "xx.x.x.x/xx" + ] + }, + "enableDdosProtection": false, + "etag": "W/\"xxxxx-xxxxx-xxxxx-xxxxx\"", + "id": "/subscriptions/xxxxxx-xxxx-xxxx-xxxxxx/resourceGroups/xx-xxxxx-xxxxx/providers/Microsoft.Network/virtualNetworks/vnet-xx-xxxxx-xxxxx", + "location": "westus", + "name": "xxxxx-xxxxx-xxxxx-xxxxx", + "provisioningState": "Succeeded", + "resourceGroup": "xx-xxxxx-xxxxx", + "resourceGuid": "xxxxx-xxxxx-xxxxx-xxxxx", + "subnets": [], + "type": "Microsoft.Network/virtualNetworks", + "virtualNetworkPeerings": [] + } +} +``` + +## Create Main Nodes Subnet + +In this section, you'll be creating the main nodes subnet with the specified name and CIDR block within your previously created Virtual Network (VNet). Start by running the az network vnet subnet create command. This process may take a few minutes. After the subnet is successfully created, you'll be ready to deploy resources into this subnet. + +```bash +az network vnet subnet create -g $RG_NAME --vnet-name $VNET_NAME -n $SUBNET1_NAME --address-prefixes 10.0.0.0/23 +``` + +Results: + + +```json +{ + "addressPrefix": "xx.x.x.x/xx", + "delegations": [], + "etag": "W/\"xxxxx-xxxxx-xxxxx-xxxxx\"", + "id": "/subscriptions/xxxxxx-xxxx-xxxx-xxxxxx/resourceGroups/xx-xxxxx-xxxxx/providers/Microsoft.Network/virtualNetworks/vnet-xx-xxxxx-xxxxx/subnets/sn-main-xxxxx", + "name": "sn-main-xxxxx", + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled", + "provisioningState": "Succeeded", + "resourceGroup": "xx-xxxxx-xxxxx", + "type": "Microsoft.Network/virtualNetworks/subnets" +} +``` + +## Create Worker Nodes Subnet + +In this section, you'll be creating a subnet for your worker nodes with the specified name and CIDR block within your previously created Virtual Network (VNet). Start by running the az network vnet subnet create command. After the subnet is successfully created, you'll be ready to deploy your worker nodes into this subnet. + +```bash +az network vnet subnet create -g $RG_NAME --vnet-name $VNET_NAME -n $SUBNET2_NAME --address-prefixes 10.0.2.0/23 +``` + +Results: + + +```json +{ + "addressPrefix": "xx.x.x.x/xx", + "delegations": [], + "etag": "W/\"xxxxx-xxxxx-xxxxx-xxxxx\"", + "id": "/subscriptions/xxxxxx-xxxx-xxxx-xxxxxx/resourceGroups/xx-xxxxx-xxxxx/providers/Microsoft.Network/virtualNetworks/vnet-xx-xxxxx-xxxxx/subnets/sn-worker-xxxxx", + "name": "sn-worker-xxxxx", + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled", + "provisioningState": "Succeeded", + "resourceGroup": "xx-xxxxx-xxxxx", + "type": "Microsoft.Network/virtualNetworks/subnets" +} +``` + +## Create Storage accounts + +This code snippet performs the following steps: + +1. Sets the `STORAGE_ACCOUNT_NAME` environment variable to a concatenation of `stor`, `LOCAL_NAME` (converted to lowercase). +2. Sets the `BARMAN_CONTAINER_NAME` environment variable to `"barman"`. +3. Creates a storage account with the specified `STORAGE_ACCOUNT_NAME` in the specified resource group. +4. Creates a storage container with the specified `BARMAN_CONTAINER_NAME` in the created storage account. + +```bash +export STORAGE_ACCOUNT_NAME="stor${LOCAL_NAME,,}" +export BARMAN_CONTAINER_NAME="barman" + +az storage account create --name "${STORAGE_ACCOUNT_NAME}" --resource-group "${RG_NAME}" --sku Standard_LRS +az storage container create --name "${BARMAN_CONTAINER_NAME}" --account-name "${STORAGE_ACCOUNT_NAME}" +``` + +## Deploy the ARO cluster + +In this section, you'll be deploying an Azure Red Hat OpenShift (ARO) cluster. The ARO_CLUSTER_NAME variable will hold the name of your ARO cluster. The az aro create command will deploy the ARO cluster with the specified name, resource group, virtual network, subnets, and the RedHat OpenShift pull secret that you previously downloaded and saved in your Key Vault. This process may take about 30 minutes to complete. + +```bash +export ARO_CLUSTER_NAME="aro-${LOCAL_NAME}" +export ARO_PULL_SECRET=$(az keyvault secret show --name AroPullSecret --vault-name kv-rdp-dev --query value -o tsv) +export ARO_SP_ID=$(az keyvault secret show --name arodemo-sp-id --vault-name kv-rdp-dev --query value -o tsv) +export ARO_SP_PASSWORD=$(az keyvault secret show --name arodemo-sp-password --vault-name kv-rdp-dev --query value -o tsv) +echo "This will take about 30 minutes to complete..." +az aro create -g $RG_NAME -n $ARO_CLUSTER_NAME --vnet $VNET_NAME --master-subnet $SUBNET1_NAME --worker-subnet $SUBNET2_NAME --tags $RGTAGS --pull-secret ${ARO_PULL_SECRET} --client-id ${ARO_SP_ID} --client-secret ${ARO_SP_PASSWORD} +``` + +Results: + +```json +{ + "apiserverProfile": { + "ip": "xx.xxx.xx.xxx", + "url": "https://api.xxxxx.xxxxxx.aroapp.io:xxxx/", + "visibility": "Public" + }, + "clusterProfile": { + "domain": "xxxxxx", + "fipsValidatedModules": "Disabled", + "pullSecret": null, + "resourceGroupId": "/subscriptions/xxxxxx-xxxxxx-xxxxxx-xxxxxx-xxxxxx/resourcegroups/xxxxxx-xxxxxx", + "version": "4.12.25" + }, + "consoleProfile": { + "url": "https://console-openshift-console.apps.xxxxxx.xxxxxx.aroapp.io/" + }, + "id": "/subscriptions/xxxxxx-xxxxxx-xxxxxx-xxxxxx-xxxxxx/resourceGroups/rg-arodemo-xxxxxx/providers/Microsoft.RedHatOpenShift/openShiftClusters/aro-arodemo-xxxxxx", + "ingressProfiles": [ + { + "ip": "xx.xxx.xx.xxx", + "name": "default", + "visibility": "Public" + } + ], + "location": "westus", + "masterProfile": { + "diskEncryptionSetId": null, + "encryptionAtHost": "Disabled", + "subnetId": "/subscriptions/xxxxxx-xxxxxx-xxxxxx-xxxxxx-xxxxxx/resourceGroups/rg-arodemo-xxxxxx/providers/Microsoft.Network/virtualNetworks/vnet-arodemo-xxxxxx/subnets/sn-main-jffspl", + "vmSize": "Standard_D8s_v3" + }, + "name": "aro-arodemo-xxxxxx", + "networkProfile": { + "outboundType": "Loadbalancer", + "podCidr": "xx.xxx.xx.xxx/xx", + "preconfiguredNsg": "Disabled", + "serviceCidr": "xx.xxx.xx.xxx/xx" + }, + "provisioningState": "Succeeded", + "resourceGroup": "rg-arodemo-xxxxxx", + "servicePrincipalProfile": { + "clientId": "xxxxxx-xxxxxx-xxxxxx-xxxxxx-xxxxxx", + "clientSecret": null + }, + "systemData": { + "createdAt": "xxxxxx-xx-xxxxxx:xx:xx.xxxxxx+xx:xx", + "createdBy": "xxxxxx@xxxxxx.xxx", + "createdByType": "User", + "lastModifiedAt": "xxxxxx-xx-xxxxxx:xx:xx.xxxxxx+xx:xx", + "lastModifiedBy": "xxxxxx@xxxxxx.xxx", + "lastModifiedByType": "User" + }, + "tags": { + "Demo": "", + "owner": "ARO" + }, + "type": "Microsoft.RedHatOpenShift/openShiftClusters", + "workerProfiles": [ + { + "count": 3, + "diskEncryptionSetId": null, + "diskSizeGb": 128, + "encryptionAtHost": "Disabled", + "name": "worker", + "subnetId": "/subscriptions/xxxxxx-xxxxxx-xxxxxx-xxxxxx-xxxxxx/resourceGroups/rg-arodemo-xxxxxx/providers/Microsoft.Network/virtualNetworks/vnet-arodemo-xxxxxx/subnets/sn-worker-xxxxxx", + "vmSize": "Standard_D4s_v3" + } + ], + "workerProfilesStatus": [ + { + "count": 3, + "diskEncryptionSetId": null, + "diskSizeGb": 128, + "encryptionAtHost": "Disabled", + "name": "aro-arodemo-xxxxxx-xxxxxx-worker-westus", + "subnetId": "/subscriptions/xxxxxx-xxxxxx-xxxxxx-xxxxxx-xxxxxx/resourceGroups/rg-arodemo-xxxxxx/providers/Microsoft.Network/virtualNetworks/vnet-arodemo-xxxxxx/subnets/sn-worker-xxxxxx", + "vmSize": "Standard_D4s_v3" + } + ] +} +``` + +## Obtain cluster credentials and login + +This code retrieves the API server URL and login credentials for an Azure Red Hat OpenShift (ARO) cluster using the Azure CLI. + +The `az aro show` command is used to get the API server URL by providing the resource group name and ARO cluster name. The `--query` parameter is used to extract the `apiserverProfile.url` property, and the `-o tsv` option is used to output the result as a tab-separated value. + +The `az aro list-credentials` command is used to get the login credentials for the ARO cluster. The `--name` parameter specifies the ARO cluster name, and the `--resource-group` parameter specifies the resource group name. The `--query` parameter is used to extract the `kubeadminPassword` property, and the `-o tsv` option is used to output the result as a tab-separated value. + +Finally, the `oc login` command is used to log in to the ARO cluster using the retrieved API server URL, the `kubeadmin` username, and the login credentials. + +```bash +export apiServer=$(az aro show -g $RG_NAME -n $ARO_CLUSTER_NAME --query apiserverProfile.url -o tsv) +export loginCred=$(az aro list-credentials --name $ARO_CLUSTER_NAME --resource-group $RG_NAME --query "kubeadminPassword" -o tsv) + +oc login $apiServer -u kubeadmin -p $loginCred --insecure-skip-tls-verify +``` + +## Add operators to ARO + +Set the namespace to install the operators to the built-in namespace `openshift-operators`. + +```bash +export NAMESPACE="openshift-operators" +``` + +Cloud Native Postgresql operator + +```bash +channelspec=$(oc get packagemanifests cloud-native-postgresql -o jsonpath="{range .status.channels[*]}Channel: {.name} currentCSV: {.currentCSV}{'\n'}{end}" | grep "stable-v1.22") +IFS=" " read -r -a array <<< "${channelspec}" +channel=${array[1]} +csv=${array[3]} + +catalogSource=$(oc get packagemanifests cloud-native-postgresql -o jsonpath="{.status.catalogSource}") +catalogSourceNamespace=$(oc get packagemanifests cloud-native-postgresql -o jsonpath="{.status.catalogSourceNamespace}") + +cat < +```text +subscription.operators.coreos.com/rhbk-operator created +``` + +## Create the ARO PosgreSQL Database + +Fetch secrets from Key Vault and create the ARO database login secret object. + +```bash +pgUserName=$(az keyvault secret show --name AroPGUser --vault-name kv-rdp-dev --query value -o tsv) +pgPassword=$(az keyvault secret show --name AroPGPassword --vault-name kv-rdp-dev --query value -o tsv) + +oc create secret generic app-auth --from-literal=username=${pgUserName} --from-literal=password=${pgPassword} -n ${NAMESPACE} +``` + +Results: + +```text +secret/app-auth created +``` + +Create the secret for backing up to Azure Storage + +```bash +export STORAGE_ACCOUNT_KEY=$(az storage account keys list --account-name ${STORAGE_ACCOUNT_NAME} --resource-group ${RG_NAME} --query "[0].value" --output tsv) +oc create secret generic azure-storage-secret --from-literal=storage-account-name=${STORAGE_ACCOUNT_NAME} --from-literal=storage-account-key=${STORAGE_ACCOUNT_KEY} --namespace ${NAMESPACE} +``` + +Results: + +```text +secret/azure-storage-secret created +``` + +Create the Postgres Cluster + +```bash +cat < +```text +cluster.postgresql.k8s.enterprisedb.io/cluster-arodemo created +``` + +## Create the ARO Keycloak instance + +Deploy a Keycloak instance on an OpenShift cluster. It uses the `oc apply` command to apply a YAML configuration file that defines the Keycloak resource. +The YAML configuration specifies various settings for the Keycloak instance, including the database, hostname, HTTP settings, ingress, number of instances, and transaction settings. +To deploy Keycloak, run this code block in a shell environment with the necessary permissions and access to the OpenShift cluster. +Note: Make sure to replace the values of the variables `$apiServer`, `$kc_hosts`, and the database credentials (`passwordSecret` and `usernameSecret`) with the appropriate values for your environment. + +```bash +export kc_hosts=$(echo $apiServer | sed -E 's/\/\/api\./\/\/apps./' | sed -En 's/.*\/\/([^:]+).*/\1/p' ) + +cat < +```text +keycloak.k8s.keycloak.org/kc001 created +``` + +Access the workload + +```bash +URL=$(ooc get ingress kc001-ingress -o json | jq -r '.spec.rules[0].host') +curl -Iv https://$URL +``` + +Results: + +```text +* Trying 104.42.132.245:443... +* Connected to kc001.apps.foppnyl9.westus.aroapp.io (104.42.132.245) port 443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.0 (OUT), TLS header, Certificate Status (22): +* TLSv1.3 (OUT), TLS handshake, Client hello (1): +* TLSv1.2 (IN), TLS header, Certificate Status (22): +* TLSv1.3 (IN), TLS handshake, Server hello (2): +``` \ No newline at end of file diff --git a/scenarios/DeployIGonAKS/README.md b/scenarios/DeployIGonAKS/README.md index 3443d24e3..1ae4ca382 100644 --- a/scenarios/DeployIGonAKS/README.md +++ b/scenarios/DeployIGonAKS/README.md @@ -14,22 +14,14 @@ ms.custom: innovation-engine Welcome to this tutorial where we will take you step by step in deploying [Inspektor Gadget](https://www.inspektor-gadget.io/) in an Azure Kubernetes Service (AKS) cluster with the kubectl plugin: `gadget`. This tutorial assumes you are logged into Azure CLI already and have selected a subscription to use with the CLI. -## Define Environment Variables +## Create a resource group -The First step in this tutorial is to define environment variables: +A resource group is a container for related resources. All resources must be placed in a resource group. We will create one for this tutorial. The following command creates a resource group with the previously defined $MY_RESOURCE_GROUP_NAME and $REGION parameters. ```bash export RANDOM_ID="$(openssl rand -hex 3)" export MY_RESOURCE_GROUP_NAME="myResourceGroup$RANDOM_ID" export REGION="eastus" -export MY_AKS_CLUSTER_NAME="myAKSCluster$RANDOM_ID" -``` - -## Create a resource group - -A resource group is a container for related resources. All resources must be placed in a resource group. We will create one for this tutorial. The following command creates a resource group with the previously defined $MY_RESOURCE_GROUP_NAME and $REGION parameters. - -```bash az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION ``` @@ -57,6 +49,7 @@ Create an AKS cluster using the az aks create command. This will take a few minutes. ```bash +export MY_AKS_CLUSTER_NAME="myAKSCluster$RANDOM_ID" az aks create \ --resource-group $MY_RESOURCE_GROUP_NAME \ --name $MY_AKS_CLUSTER_NAME \ diff --git a/scenarios/DeployLLMWithTorchserveOnAKS/Dockerfile b/scenarios/DeployLLMWithTorchserveOnAKS/Dockerfile new file mode 100644 index 000000000..2d874b4a8 --- /dev/null +++ b/scenarios/DeployLLMWithTorchserveOnAKS/Dockerfile @@ -0,0 +1,10 @@ +FROM pytorch/torchserve:latest + +# Copy the model archive into the model store +COPY llm_model.mar /home/model-server/model-store/ + +# Expose TorchServe ports +EXPOSE 8080 8081 + +# Start TorchServe +CMD ["torchserve", "--start", "--model-store", "/home/model-server/model-store", "--models", "llm_model.mar"] \ No newline at end of file diff --git a/scenarios/DeployLLMWithTorchserveOnAKS/deploy-llm-with-torchserve-on-aks.md b/scenarios/DeployLLMWithTorchserveOnAKS/deploy-llm-with-torchserve-on-aks.md new file mode 100644 index 000000000..855dd509b --- /dev/null +++ b/scenarios/DeployLLMWithTorchserveOnAKS/deploy-llm-with-torchserve-on-aks.md @@ -0,0 +1,288 @@ +--- +title: 'Quickstart: Deploy a Large Language Model with TorchServe on Azure Kubernetes Service (AKS)' +description: Learn how to deploy a large language model using TorchServe on AKS. +ms.topic: quickstart +ms.date: 10/18/2023 +author: placeholder +ms.author: placeholder +ms.custom: devx-track-azurecli, mode-api, innovation-engine, linux-related-content +--- + +# Quickstart: Deploy a Large Language Model with TorchServe on Azure Kubernetes Service (AKS) + +In this quickstart, you will learn how to deploy a large language model (LLM) using TorchServe on Azure Kubernetes Service (AKS). TorchServe is a flexible and easy-to-use tool for serving PyTorch models at scale. + +## Prerequisites + +- An Azure subscription. If you don't have an Azure subscription, create a [free account](https://azure.microsoft.com/free/). +- Azure CLI installed. To install, see [Install Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli). +- Kubernetes CLI (`kubectl`) installed. To install, see [Install kubectl](https://kubernetes.io/docs/tasks/tools/). +- Docker installed. To install, see [Install Docker](https://docs.docker.com/get-docker/). +- Basic knowledge of Docker, Kubernetes, and AKS. + +## Create a Resource Group + +Create a resource group with the `az group create` command. + +```bash +export RANDOM_ID="$(openssl rand -hex 3)" +export RESOURCE_GROUP="LLMResourceGroup$RANDOM_ID" +export REGION="westus2" +az group create --name $RESOURCE_GROUP --location $REGION +``` + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/LLMResourceGroupxxxxxx", + "location": "eastus", + "managedBy": null, + "name": "LLMResourceGroupxxxxxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Create an Azure Container Registry + +Create an Azure Container Registry (ACR) to store your Docker images. + +```bash +export ACR_NAME="llmacr$RANDOM_ID" +az acr create --resource-group $RESOURCE_GROUP --name $ACR_NAME --sku Basic +``` + +Results: + + + +```json +{ + "adminUserEnabled": false, + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/LLMResourceGroupxxxxxx/providers/Microsoft.ContainerRegistry/registries/llmacrxxxxxx", + "location": "eastus", + "loginServer": "llmacrxxxxxx.azurecr.io", + "name": "llmacrxxxxxx", + "provisioningState": "Succeeded", + "resourceGroup": "LLMResourceGroupxxxxxx", + "sku": { + "name": "Basic", + "tier": "Basic" + }, + "type": "Microsoft.ContainerRegistry/registries" +} +``` + +## Create an AKS Cluster + +Create an AKS cluster and attach the ACR. + +```bash +export AKS_CLUSTER="LLMAKSCluster$RANDOM_ID" + +az aks create \ + --resource-group $RESOURCE_GROUP \ + --name $AKS_CLUSTER \ + --node-count 3 \ + --attach-acr $ACR_NAME +``` + +This command may take several minutes to complete. + +## Connect to the Cluster + +Configure `kubectl` to connect to your Kubernetes cluster. + +```bash +az aks get-credentials --resource-group $RESOURCE_GROUP --name $AKS_CLUSTER +``` + +Verify the connection by listing the cluster nodes. + +```bash +kubectl get nodes +``` + +## Build and Push the Docker Image + +### Prepare Model Artifacts + +Place your model artifacts in the same directory as this markdown file. Ensure the following files are present: + +- `model.py`: Your PyTorch model definition. +- `model.pt`: Your trained model weights. +- `handler.py`: A custom handler for TorchServe. +- `requirements.txt`: Any additional Python dependencies. + +### Create a Model Archive + +Generate a TorchServe model archive (`.mar` file). + +```bash +torch-model-archiver \ + --model-name llm_model \ + --version 1.0 \ + --model-file model.py \ + --serialized-file model.pt \ + --handler handler.py \ + --extra-files requirements.txt +``` + +### Create a Dockerfile + +Create a file named `Dockerfile` in the same directory with the following content: + +```dockerfile +FROM pytorch/torchserve:latest + +# Copy the model archive into the model store +COPY llm_model.mar /home/model-server/model-store/ + +# Expose TorchServe ports +EXPOSE 8080 8081 + +# Start TorchServe +CMD ["torchserve", "--start", "--model-store", "/home/model-server/model-store", "--models", "llm_model.mar"] +``` + +### Build the Docker Image + +Build the Docker image and tag it with your ACR login server. + +```bash +export ACR_LOGIN_SERVER=$(az acr show --name $ACR_NAME --query loginServer -o tsv) +export IMAGE_TAG="$ACR_LOGIN_SERVER/llm-torchserve:latest" +docker build -t $IMAGE_TAG . +``` + +### Push the Image to ACR + +Log in to ACR and push the image. + +```bash +az acr login --name $ACR_NAME +docker push $IMAGE_TAG +``` + +## Deploy the Docker Image to AKS + +### Assign the `AcrPull` Role to the AKS Cluster's Managed Identity + +```bash +AKS_RESOURCE_GROUP=$RESOURCE_GROUP +AKS_CLUSTER_NAME=$AKS_CLUSTER + +# Get the managed identity's object ID +OBJECT_ID=$(az aks show \ + --resource-group $AKS_RESOURCE_GROUP \ + --name $AKS_CLUSTER_NAME \ + --query "identityProfile.kubeletidentity.objectId" \ + --output tsv) + +# Assign the AcrPull role using the object ID +az role assignment create \ + --assignee-object-id $OBJECT_ID \ + --assignee-principal-type ServicePrincipal \ + --role AcrPull \ + --scope $(az acr show --name $ACR_NAME --query id --output tsv) +``` + +### Create a Kubernetes Deployment + +Create a Kubernetes deployment file named `torchserve-deployment.yaml` in the same directory and add the following content: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: torchserve-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: torchserve + template: + metadata: + labels: + app: torchserve + spec: + containers: + - name: torchserve-container + image: $IMAGE_TAG + ports: + - containerPort: 8080 +``` + +Apply the deployment: + +```bash +kubectl apply -f torchserve-deployment.yaml +``` + +## Expose the Service + +Create a service file named `torchserve-service.yaml` in the same directory with the following content: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: torchserve-service +spec: + type: LoadBalancer + ports: + - port: 80 + targetPort: 8080 + selector: + app: torchserve +``` + +Apply the service: + +```bash +kubectl apply -f torchserve-service.yaml +``` + +## Test the Deployment + +Wait for the external IP to become available: + +```bash +kubectl get service torchserve-service +``` + +Once the `EXTERNAL-IP` is assigned, you can test the deployment: + +```bash +export SERVICE_IP=$(kubectl get service torchserve-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +kubectl get service torchserve-service --watch +curl http://$SERVICE_IP/ping +``` + +Results: + + + +```json +{ + "status": "Healthy" +} +``` + +Invoke the model inference endpoint: + +```bash +curl -X POST http://$SERVICE_IP/predictions/llm_model -T input.json +``` + +Replace `input.json` with your input data file. + +## Next Steps + +In this quickstart, you deployed a large language model using TorchServe on AKS. You can now scale your deployment, monitor performance, and integrate with other Azure services. \ No newline at end of file diff --git a/scenarios/DeployLLMWithTorchserveOnAKS/handler.py b/scenarios/DeployLLMWithTorchserveOnAKS/handler.py new file mode 100644 index 000000000..1539b4ae6 --- /dev/null +++ b/scenarios/DeployLLMWithTorchserveOnAKS/handler.py @@ -0,0 +1,12 @@ +from ts.torch_handler.base_handler import BaseHandler +import torch + +class SimpleHandler(BaseHandler): + def preprocess(self, data): + return torch.tensor(data[0]['body']) + + def inference(self, input_data): + return self.model(input_data).detach().numpy() + + def postprocess(self, inference_output): + return inference_output.tolist() \ No newline at end of file diff --git a/scenarios/DeployLLMWithTorchserveOnAKS/model.pt b/scenarios/DeployLLMWithTorchserveOnAKS/model.pt new file mode 100644 index 000000000..a142ea17e --- /dev/null +++ b/scenarios/DeployLLMWithTorchserveOnAKS/model.pt @@ -0,0 +1,5 @@ +import torch +from model import SimpleModel + +model = SimpleModel() +torch.save(model, 'model.pt') \ No newline at end of file diff --git a/scenarios/DeployLLMWithTorchserveOnAKS/model.py b/scenarios/DeployLLMWithTorchserveOnAKS/model.py new file mode 100644 index 000000000..46e71f58b --- /dev/null +++ b/scenarios/DeployLLMWithTorchserveOnAKS/model.py @@ -0,0 +1,9 @@ +import torch.nn as nn + +class SimpleModel(nn.Module): + def __init__(self): + super(SimpleModel, self).__init__() + self.linear = nn.Linear(10, 1) + + def forward(self, x): + return self.linear(x) \ No newline at end of file diff --git a/scenarios/DeployLLMWithTorchserveOnAKS/requirements.txt b/scenarios/DeployLLMWithTorchserveOnAKS/requirements.txt new file mode 100644 index 000000000..262c93aea --- /dev/null +++ b/scenarios/DeployLLMWithTorchserveOnAKS/requirements.txt @@ -0,0 +1,4 @@ +torch +torchserve +numpy +torch-model-archiver \ No newline at end of file diff --git a/scenarios/DeployLLMWithTorchserveOnAKS/torchserve-deployment.yaml b/scenarios/DeployLLMWithTorchserveOnAKS/torchserve-deployment.yaml new file mode 100644 index 000000000..57ea39859 --- /dev/null +++ b/scenarios/DeployLLMWithTorchserveOnAKS/torchserve-deployment.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: torchserve-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: torchserve + template: + metadata: + labels: + app: torchserve + spec: + containers: + - name: torchserve-container + image: $IMAGE_TAG + ports: + - containerPort: 8080 \ No newline at end of file diff --git a/scenarios/DeployLLMWithTorchserveOnAKS/torchserve-service.yaml b/scenarios/DeployLLMWithTorchserveOnAKS/torchserve-service.yaml new file mode 100644 index 000000000..a555137b8 --- /dev/null +++ b/scenarios/DeployLLMWithTorchserveOnAKS/torchserve-service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: torchserve-service +spec: + type: LoadBalancer + ports: + - port: 80 + targetPort: 8080 + selector: + app: torchserve \ No newline at end of file diff --git a/scenarios/DeployPremiumSSDV2/deploy-premium-ssd-v2.md b/scenarios/DeployPremiumSSDV2/deploy-premium-ssd-v2.md new file mode 100644 index 000000000..86ae6d81f --- /dev/null +++ b/scenarios/DeployPremiumSSDV2/deploy-premium-ssd-v2.md @@ -0,0 +1,266 @@ +--- +title: Deploy a Premium SSD v2 managed disk +description: Learn how to deploy a Premium SSD v2 and about its regional availability. +author: roygara +ms.author: rogarana +ms.date: 08/12/2024 +ms.topic: how-to +ms.service: azure-disk-storage +ms.custom: references_regions, devx-track-azurecli, devx-track-azurepowershell +--- + +# Deploy a Premium SSD v2 + +Azure Premium SSD v2 is designed for IO-intense enterprise workloads that require sub-millisecond disk latencies and high IOPS and throughput at a low cost. Premium SSD v2 is suited for a broad range of workloads such as SQL server, Oracle, MariaDB, SAP, Cassandra, Mongo DB, big data/analytics, gaming, on virtual machines or stateful containers. For conceptual information on Premium SSD v2, see [Premium SSD v2](disks-types.md#premium-ssd-v2). + +Premium SSD v2 support a 4k physical sector size by default, but can be configured to use a 512E sector size as well. While most applications are compatible with 4k sector sizes, some require 512 byte sector sizes. Oracle Database, for example, requires release 12.2 or later in order to support 4k native disks. + +## Limitations + +[!INCLUDE [disks-prem-v2-limitations](./includes/disks-prem-v2-limitations.md)] + +### Regional availability + +[!INCLUDE [disks-premv2-regions](./includes/disks-premv2-regions.md)] + +## Prerequisites + +- Install either the latest [Azure CLI](/cli/azure/install-azure-cli) or the latest [Azure PowerShell module](/powershell/azure/install-azure-powershell). + +## Determine region availability programmatically + +Since not every region and zone supports Premium SSD v2, you can use the Azure CLI or PowerShell to determine region and zone supportability. + +# [Azure CLI](#tab/azure-cli) + +To determine the regions and zones that support Premium SSD v2, replace `yourSubscriptionId` with your subscription, and then run the [az vm list-skus](/cli/azure/vm#az-vm-list-skus) command: + +```markdown +az login + +subscriptionId="" + +az account set --subscription $subscriptionId + +az vm list-skus --resource-type disks --query "[?name=='PremiumV2_LRS'].{Region:locationInfo[0].location, Zones:locationInfo[0].zones}" +``` + +# [PowerShell](#tab/azure-powershell) + +To determine the regions and zones that support Premium SSD v2, replace `yourSubscriptionId` with your subscription, and then run the [Get-AzComputeResourceSku](/powershell/module/az.compute/get-azcomputeresourcesku) command: + +```powershell +Connect-AzAccount + +$subscriptionId="yourSubscriptionId" + +Set-AzContext -Subscription $subscriptionId + +Get-AzComputeResourceSku | where {$_.ResourceType -eq 'disks' -and $_.Name -eq 'Premiumv2_LRS'} +``` + +# [Azure portal](#tab/portal) + +To programmatically determine the regions and zones you can deploy to, use either the Azure CLI, Azure PowerShell Module. + +--- + +## Create a resource group + +An [Azure resource group][azure-resource-group] is a logical group in which Azure resources are deployed and managed. When you create a resource group, you're prompted to specify a location. This location is the storage location of your resource group metadata and where your resources run in Azure if you don't specify another region during resource creation + +Create a resource group using the [`az group create`][az-group-create] command. + +```azurecli-interactive +export RANDOM_ID="$(openssl rand -hex 3)" +export MY_RESOURCE_GROUP_NAME="myResourceGroup$RANDOM_ID" +export REGION="eastus2" +az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION +``` + +Results: + +```JSON +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myAKSResourceGroupxxxxxx", + "location": "eastus", + "managedBy": null, + "name": "testResourceGroup", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +Now that you know the region and zone to deploy to, follow the deployment steps in this article to create a Premium SSD v2 disk and attach it to a VM. + +## Use a Premium SSD v2 + +Create a Premium SSD v2 disk in an availability zone by using the [az disk create](/cli/azure/disk#az-disk-create) command. + +The following script creates a Premium SSD v2 with a 4k sector size, to deploy one with a 512 sector size, update the `$LOGICAL_SECTOR_SIZE` parameter. Replace the values of all the variables with your own, then run the following script: + +```azurecli-interactive +## Create a Premium SSD v2 disk +export MY_DISK_NAME="myDisk$RANDOM_ID" +##Replace 4096 with 512 to deploy a disk with 512 sector size +export LOGICAL_SECTOR_SIZE=4096 +az disk create -n $MY_DISK_NAME -g $MY_RESOURCE_GROUP_NAME \ +--size-gb 100 \ +--disk-iops-read-write 5000 \ +--disk-mbps-read-write 150 \ +--location $REGION \ +--sku PremiumV2_LRS \ +--zone "1" \ +--logical-sector-size $LOGICAL_SECTOR_SIZE +``` + +## Create the VM + +Then create a VM in the same region and availability zone that supports Premium Storage and attach the disk to it by using the [az vm create](/cli/azure/vm#az-vm-create) command. + +```azurecli-interactive +export MY_VM_NAME="myVM$RANDOM_ID" +export MY_VM_IMAGE="Win2016Datacenter" +export MY_VM_SIZE="Standard_D4s_v3" +export AZURE_USERNAME=azureuser +export AZURE_PASSWORD=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9@#%^&*()-_=+[]{}|;:,.<>?') +az vm create -n $MY_VM_NAME -g $MY_RESOURCE_GROUP_NAME \ +--image $MY_VM_IMAGE \ +--authentication-type password --admin-password $AZURE_PASSWORD --admin-username $AZURE_USERNAME \ +--size $MY_VM_SIZE \ +--location $REGION \ +--zone "1" \ +--attach-data-disks $MY_DISK_NAME +``` + +# [PowerShell](#tab/azure-powershell) + +Create a Premium SSD v2 disk in an availability zone by using the [New-AzDiskConfig](/powershell/module/az.compute/new-azdiskconfig) to define the configuration of your disk and the [New-AzDisk](/powershell/module/az.compute/new-azdisk) command to create your disk. Next, create a VM in the same region and availability zone that supports Premium Storage by using the [az vm create](/cli/azure/vm#az-vm-create). Finally, attach the disk to it by using the [Get-AzVM](/powershell/module/az.compute/get-azvm) command to identify variables for the virtual machine, the [Get-AzDisk](/powershell/module/az.compute/get-azdisk) command to identify variables for the disk, the [Add-AzVMDataDisk](/powershell/module/az.compute/add-azvmdatadisk) command to add the disk, and the [Update-AzVM](/powershell/module/az.compute/update-azvm) command to attach the new disk to the virtual machine. + +The following script creates a Premium SSD v2 with a 4k sector size, to deploy one with a 512 sector size, update the `$LOGICAL_SECTOR_SIZE` parameter. Replace the values of all the variables with your own, then run the following script: + +```powershell +# Initialize variables +$MY_RESOURCE_GROUP_NAME = "yourResourceGroupName" +$REGION = "useast" +$zone = "yourZoneNumber" +$MY_DISK_NAME = "yourMY_DISK_NAME" +$diskSizeInGiB = 100 +$diskIOPS = 5000 +$diskThroughputInMBPS = 150 +#To use a 512 sector size, replace 4096 with 512 +$LOGICAL_SECTOR_SIZE=4096 +$lun = 1 +$MY_VM_NAME = "yourMY_VM_NAME" +$MY_VM_IMAGE = "Win2016Datacenter" +$MY_VM_SIZE = "Standard_D4s_v3" +$vmAdminUser = "yourAdminUserName" +$vmAdminPassword = ConvertTo-SecureString "yourAdminUserPassword" -AsPlainText -Force +$credential = New-Object System.Management.Automation.PSCredential ($vmAdminUser, $vmAdminPassword); + +# Create a Premium SSD v2 +$diskconfig = New-AzDiskConfig ` +-Location $REGION ` +-Zone $zone ` +-DiskSizeGB $diskSizeInGiB ` +-DiskIOPSReadWrite $diskIOPS ` +-DiskMBpsReadWrite $diskThroughputInMBPS ` +-AccountType PremiumV2_LRS ` +-LOGICAL_SECTOR_SIZE $LOGICAL_SECTOR_SIZE ` +-CreateOption Empty + +New-AzDisk ` +-ResourceGroupName $MY_RESOURCE_GROUP_NAME ` +-MY_DISK_NAME $MY_DISK_NAME ` +-Disk $diskconfig + +# Create the VM +New-AzVm ` + -ResourceGroupName $MY_RESOURCE_GROUP_NAME ` + -Name $MY_VM_NAME ` + -Location $REGION ` + -Zone $zone ` + -Image $MY_VM_IMAGE ` + -Size $MY_VM_SIZE ` + -Credential $credential + +# Attach the disk to the VM +$vm = Get-AzVM -ResourceGroupName $MY_RESOURCE_GROUP_NAME -Name $MY_VM_NAME +$disk = Get-AzDisk -ResourceGroupName $MY_RESOURCE_GROUP_NAME -Name $MY_DISK_NAME +$vm = Add-AzVMDataDisk -VM $vm -Name $MY_DISK_NAME -CreateOption Attach -ManagedDiskId $disk.Id -Lun $lun +Update-AzVM -VM $vm -ResourceGroupName $MY_RESOURCE_GROUP_NAME +``` + +# [Azure portal](#tab/portal) + +1. Sign in to the [Azure portal](https://portal.azure.com/). +1. Navigate to **Virtual machines** and follow the normal VM creation process. +1. On the **Basics** page, select a [supported region](#regional-availability) and set **Availability options** to **Availability zone**. +1. Select one of the zones. +1. Fill in the rest of the values on the page as you like. + + :::image type="content" source="media/disks-deploy-premium-v2/premv2-portal-deploy.png" alt-text="Screenshot of the basics page, region and availability options and zones highlighted." lightbox="media/disks-deploy-premium-v2/premv2-portal-deploy.png"::: + +1. Proceed to the **Disks** page. +1. Under **Data disks** select **Create and attach a new disk**. + + :::image type="content" source="media/disks-deploy-premium-v2/premv2-create-data-disk.png" alt-text="Screenshot highlighting create and attach a new disk on the disk page." lightbox="media/disks-deploy-premium-v2/premv2-create-data-disk.png"::: + +1. Select the **Disk SKU** and select **Premium SSD v2**. + + :::image type="content" source="media/disks-deploy-premium-v2/premv2-select.png" alt-text="Screenshot selecting Premium SSD v2 SKU." lightbox="media/disks-deploy-premium-v2/premv2-select.png"::: + +1. Select whether you'd like to deploy a 4k or 512 logical sector size. + + :::image type="content" source="media/disks-deploy-premium-v2/premv2-sector-size.png" alt-text="Screenshot of deployment logical sector size deployment options." lightbox="media/disks-deploy-premium-v2/premv2-sector-size.png"::: + +1. Proceed through the rest of the VM deployment, making any choices that you desire. + +You've now deployed a VM with a premium SSD v2. + +--- + +## Adjust disk performance + +You can adjust the performance of a Premium SSD v2 disk four times within a 24 hour period. Creating a disk counts as one of these times, so for the first 24 hours after creating a premium SSD v2 disk you can only adjust its performance up to three times. + +For conceptual information on adjusting disk performance, see [Premium SSD v2 performance](disks-types.md#premium-ssd-v2-performance). + +# [Azure CLI](#tab/azure-cli) + +Use the [az disk update](/cli/azure/disk#az-disk-update) command to change the performance configuration of your Premium SSD v2 disk. For example, you can use the `disk-iops-read-write` parameter to adjust the max IOPS limit, and the `disk-mbps-read-write` parameter to adjust the max throughput limit of your Premium SSD v2 disk. + +The following command adjusts the performance of your disk. Update the values in the command, and then run it: + +```azurecli +export SUBSCRIPTION_ID=$(az account show --query id --output tsv) +az disk update --subscription $SUBSCRIPTION_ID --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_DISK_NAME --disk-iops-read-write=5000 --disk-mbps-read-write=200 +``` + +# [PowerShell](#tab/azure-powershell) + +Use the [New-AzDiskUpdateConfig](/powershell/module/az.compute/new-azdiskupdateconfig) command to define your new performance configuration values for your Premium SSD v2 disks, and then use the [Update-AzDisk](/powershell/module/az.compute/update-azdisk) command to apply your configuration changes to your disk. For example, you can use the `DiskIOPSReadWrite` parameter to adjust the max IOPS limit, and the `DiskMBpsReadWrite` parameter to adjust the max throughput limit of your Premium SSD v2 disk. + +The following command adjusts the performance of your disk. Update the values in the command, and then run it: + +```azurepowershell +$diskupdateconfig = New-AzDiskUpdateConfig -DiskIOPSReadWrite 5000 -DiskMBpsReadWrite 200 +Update-AzDisk -ResourceGroupName $resourceGroup -MY_DISK_NAME $MY_DISK_NAME -DiskUpdate $diskupdateconfig +``` + +# [Azure portal](#tab/portal) + +1. Navigate to the disk you'd like to modify in the [Azure portal](https://portal.azure.com/). +1. Select **Size + Performance** +1. Set the values for **Disk IOPS** or **Disk throughput (MB/s)** or both, to meet your needs, then select **Save**. + +--- + +## Next steps + +Add a data disk by using either the [Azure portal](linux/attach-disk-portal.yml), [Azure CLI](linux/add-disk.md), or [PowerShell](windows/attach-disk-ps.md). + +Provide feedback on [Premium SSD v2](https://aka.ms/premium-ssd-v2-survey). \ No newline at end of file diff --git a/scenarios/DeployTensorflowOnAKS/deploy-tensorflow-on-aks.md b/scenarios/DeployTensorflowOnAKS/deploy-tensorflow-on-aks.md new file mode 100644 index 000000000..b7998ea1d --- /dev/null +++ b/scenarios/DeployTensorflowOnAKS/deploy-tensorflow-on-aks.md @@ -0,0 +1,191 @@ +--- +title: 'Setup: Deploy a Tensorflow Cluster on Azure Kubernetes Service (AKS)' +description: Learn how to deploy a Tensorflow cluster on Azure Kubernetes Service (AKS) using Azure CLI. +ms.topic: how-to +ms.date: 10/31/2023 +author: azureexecdocs +ms.author: azureexecdocs +ms.custom: devx-track-azurecli, mode-api, innovation-engine, machine-learning, kubernetes +--- + +# Setup: Deploy a Tensorflow Cluster on Azure Kubernetes Service (AKS) + +This guide demonstrates how to deploy a Tensorflow cluster on AKS using the Azure CLI. The setup includes provisioning an AKS cluster, configuring a Kubernetes namespace, and deploying a TensorFlow cluster. + + +## Prerequisites + +- Azure CLI (version 2.40.0 or later) +- Kubernetes CLI (kubectl) installed and configured with the Azure AKS cluster +- Bash shell with OpenSSL for generating random suffixes + +> **Note:** Please make sure you are logged into Azure and have set your subscription in advance. + + +## Step 1: Create a Resource Group + +Create a new resource group to hold your AKS cluster. + +```bash +export RANDOM_SUFFIX=$(openssl rand -hex 3) +export REGION="WestUS2" +export RESOURCE_GROUP_NAME="AKS-TF-ResourceGroup-$RANDOM_SUFFIX" +az group create --name $RESOURCE_GROUP_NAME --location $REGION +``` + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/AKS-TF-ResourceGroup-xxx", + "location": "westus2", + "managedBy": null, + "name": "AKS-TF-ResourceGroup-xxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Step 2: Create an AKS Cluster + +Provision an AKS cluster in the resource group. + +```bash +export AKS_CLUSTER_NAME="AKS-TF-Cluster-$RANDOM_SUFFIX" +az aks create --name $AKS_CLUSTER_NAME --resource-group $RESOURCE_GROUP_NAME --node-count 3 --enable-addons monitoring --generate-ssh-keys +``` + + +## Step 3: Connect to the AKS Cluster + +Obtain the cluster credentials and configure `kubectl` to use the newly created AKS cluster. + +```bash +az aks get-credentials --name $AKS_CLUSTER_NAME --resource-group $RESOURCE_GROUP_NAME +``` + +## Step 4: Create a Kubernetes Namespace for TensorFlow + +Create a namespace to organize resources related to TensorFlow. + +```bash +export NAMESPACE="tensorflow-cluster" +kubectl create namespace $NAMESPACE +``` + +Results: + + + +```text +namespace/tensorflow-cluster created +``` + +## Step 5: Prepare TensorFlow Deployment Configuration + +Create the TensorFlow deployment configuration file. + +```bash +cat < tensorflow-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tensorflow-deployment + namespace: $NAMESPACE +spec: + replicas: 2 + selector: + matchLabels: + app: tensorflow + template: + metadata: + labels: + app: tensorflow + spec: + containers: + - name: tensorflow-container + image: tensorflow/tensorflow:latest + ports: + - containerPort: 8501 +EOF +``` + +## Step 6: Deploy the TensorFlow Cluster + +Deploy the TensorFlow cluster by applying the configuration file. + +```bash +kubectl apply -f tensorflow-deployment.yaml +``` + +Results: + + + +```text +deployment.apps/tensorflow-deployment created +``` + +## Step 7: Create a LoadBalancer Service for TensorFlow + +Expose the TensorFlow deployment using a LoadBalancer service to make it accessible externally. + +```bash +cat < tensorflow-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: tensorflow-service + namespace: $NAMESPACE +spec: + selector: + app: tensorflow + ports: + - protocol: TCP + port: 80 + targetPort: 8501 + type: LoadBalancer +EOF + +kubectl apply -f tensorflow-service.yaml +``` + +Results: + + + +```text +service/tensorflow-service created +``` + +## Step 8: Check Service External IP + +Retrieve the external IP address of the TensorFlow service. + +```bash +while true; do + ENDPOINTS=$(kubectl get endpoints tensorflow-service --namespace $NAMESPACE -o jsonpath='{.subsets[*].addresses[*].ip}') + if [ -n "$ENDPOINTS" ]; then + echo "Service endpoints: $ENDPOINTS" + break + else + echo "Waiting for service endpoints..." + sleep 10 + fi +done +``` + +Results: + + + +```text +Service endpoints: 10.244.1.5 10.244.1.6 +``` + +This confirms that the service is routing correctly to its backend pods. \ No newline at end of file diff --git a/scenarios/DeployTrinoOnAKS/deploy-trino-on-aks.md b/scenarios/DeployTrinoOnAKS/deploy-trino-on-aks.md new file mode 100644 index 000000000..22ea52146 --- /dev/null +++ b/scenarios/DeployTrinoOnAKS/deploy-trino-on-aks.md @@ -0,0 +1,222 @@ +--- +title: "Deploy a Trino Cluster on Azure Kubernetes Service (AKS)" +description: Learn how to deploy a Trino Cluster on AKS using Azure CLI for scalable and distributed SQL query processing. +ms.topic: article +ms.date: 10/10/2023 +author: azure-author +ms.author: azurealias +ms.custom: devx-track-azurecli, mode-api, innovation-engine, aks, trino, distributed-sql, data-analytics +--- + +# Deploy a Trino Cluster on Azure Kubernetes Service (AKS) + +In this Exec Doc, you will learn how to deploy a Trino (formerly PrestoSQL) cluster on Azure Kubernetes Service (AKS). Trino is a distributed SQL query engine, ideal for large-scale data analytics. + +## Prerequisites + +1. Ensure you have Azure CLI installed in your environment or use [Azure Cloud Shell](https://shell.azure.com/). +2. Ensure a Kubernetes cluster is already deployed on AKS. You can create one using [this guide](https://learn.microsoft.com/azure/aks/). + + +## Step 2: Create Azure Resource Group + +A resource group is a container that holds related resources for the Trino deployment. + +```bash +export RANDOM_SUFFIX=$(openssl rand -hex 3) +export RESOURCE_GROUP_NAME="TrinoResourceGroup$RANDOM_SUFFIX" +export REGION="westus2" + +az group create --name $RESOURCE_GROUP_NAME --location $REGION +``` + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxx-xxxxx-xxxxx-xxxxx/resourceGroups/TrinoResourceGroupxxx", + "location": "westus2", + "managedBy": null, + "name": "TrinoResourceGroupxxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Step 3: Create AKS Cluster + +We will deploy an AKS cluster to host the Trino cluster. + +```bash +export AKS_CLUSTER_NAME="TrinoAKSCluster$RANDOM_SUFFIX" +export CLUSTER_NODES=3 + +az aks create \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $AKS_CLUSTER_NAME \ + --node-count $CLUSTER_NODES \ + --generate-ssh-keys +``` + +## Step 4: Configure `kubectl` Access + +We will configure `kubectl` to connect to the newly created AKS cluster. + +```bash +az aks get-credentials --resource-group $RESOURCE_GROUP_NAME --name $AKS_CLUSTER_NAME +``` + +## Step 5: Create Namespace for Trino + +Namespaces help organize your Kubernetes resources. + +```bash +export NAMESPACE="trino$RANDOM_SUFFIX" +kubectl create namespace $NAMESPACE +``` + +Results: + + + +```json +{ + "kind": "Namespace", + "apiVersion": "v1", + "metadata": { + "name": "trino", + "selfLink": "/api/v1/namespaces/trino", + "uid": "xxxxx-xxxxx-xxxxx-xxxxx", + "resourceVersion": "xxxx", + "creationTimestamp": "xxxx-xx-xxTxx:xx:xxZ" + } +} +``` + +## Step 6: Deploy Trino on AKS + +We will use a Kubernetes manifest to deploy the Trino cluster. + +### Create `trino-deployment.yaml` + +```bash +cat < trino-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: trino + namespace: $NAMESPACE +spec: + replicas: 2 + selector: + matchLabels: + app: trino + template: + metadata: + labels: + app: trino + spec: + containers: + - name: trino + image: trinodb/trino:latest + ports: + - containerPort: 8080 +EOF +``` + +### Apply the Deployment + +```bash +kubectl apply -f trino-deployment.yaml +``` + +Results: + + + +```text +deployment.apps/trino created +``` + +## Step 7: Expose Trino Service + +Expose the Trino deployment via a Kubernetes service for external access. + +```bash +kubectl expose deployment trino \ + --type=LoadBalancer \ + --name=trino-service \ + --namespace=$NAMESPACE \ + --port=8080 \ + --target-port=8080 +``` + +Results: + + + +```output +service/trino-service exposed +``` + + +## Step 8: Verify Deployment + +Ensure that all Trino pods are running. + +```bash +while true; do + POD_STATUSES=$(kubectl get pods --namespace=$NAMESPACE -o jsonpath='{.items[*].status.phase}') + ALL_RUNNING=true + for STATUS in $POD_STATUSES; do + if [ "$STATUS" != "Running" ]; then + ALL_RUNNING=false + break + fi + done + + if [ "$ALL_RUNNING" = true ]; then + kubectl get pods --namespace=$NAMESPACE + break + else + sleep 10 + fi +done +``` + +Results: + + + +```text +NAME READY STATUS RESTARTS AGE +trino-xxxxx-xxxxx 1/1 Running 0 5m +trino-xxxxx-xxxxx 1/1 Running 0 5m +``` + +## Step 9: Fetch Service Public IP + +Retrieve the external IP address of the Trino service. + +```bash +EXTERNAL_IP=$(kubectl get service trino-service --namespace=$NAMESPACE -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +echo "External IP: $EXTERNAL_IP" +``` + +Results: + + + +```text +External IP: xx.xx.xx.xx +``` + +The `EXTERNAL-IP` field contains the Trino service's public IP. Visit `http://:8080` to access the Trino cluster. + + +You have successfully deployed a Trino cluster on Azure Kubernetes Service! 🎉 \ No newline at end of file diff --git a/scenarios/GPUNodePoolAKS/gpu-node-pool-aks.md b/scenarios/GPUNodePoolAKS/gpu-node-pool-aks.md new file mode 100644 index 000000000..516aa2783 --- /dev/null +++ b/scenarios/GPUNodePoolAKS/gpu-node-pool-aks.md @@ -0,0 +1,516 @@ +--- +title: Create a multi-instance GPU node pool in Azure Kubernetes Service (AKS) +description: Learn how to create a multi-instance GPU node pool in Azure Kubernetes Service (AKS). +ms.topic: article +ms.date: 08/30/2023 +ms.author: juda +ms.subservice: aks-nodes +--- + +# Create a multi-instance GPU node pool in Azure Kubernetes Service (AKS) + +Nvidia's A100 GPU can be divided in up to seven independent instances. Each instance has its own memory and Stream Multiprocessor (SM). For more information on the Nvidia A100, see [Nvidia A100 GPU][Nvidia A100 GPU]. + +This article walks you through how to create a multi-instance GPU node pool in an Azure Kubernetes Service (AKS) cluster. + +## Prerequisites and limitations + +* An Azure account with an active subscription. If you don't have one, you can [create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). +* Azure CLI version 2.2.0 or later installed and configured. Run `az --version` to find the version. If you need to install or upgrade, see [Install Azure CLI][install-azure-cli]. +* The Kubernetes command-line client, [kubectl](https://kubernetes.io/docs/reference/kubectl/), installed and configured. If you use Azure Cloud Shell, `kubectl` is already installed. If you want to install it locally, you can use the [`az aks install-cli`][az-aks-install-cli] command. +* Helm v3 installed and configured. For more information, see [Installing Helm](https://helm.sh/docs/intro/install/). +* You can't use Cluster Autoscaler with multi-instance node pools. + +## GPU instance profiles + +GPU instance profiles define how GPUs are partitioned. The following table shows the available GPU instance profile for the `Standard_ND96asr_v4`: + +| Profile name | Fraction of SM |Fraction of memory | Number of instances created | +|--|--|--|--| +| MIG 1g.5gb | 1/7 | 1/8 | 7 | +| MIG 2g.10gb | 2/7 | 2/8 | 3 | +| MIG 3g.20gb | 3/7 | 4/8 | 2 | +| MIG 4g.20gb | 4/7 | 4/8 | 1 | +| MIG 7g.40gb | 7/7 | 8/8 | 1 | + +As an example, the GPU instance profile of `MIG 1g.5gb` indicates that each GPU instance has 1g SM(Computing resource) and 5gb memory. In this case, the GPU is partitioned into seven instances. + +The available GPU instance profiles available for this instance size include `MIG1g`, `MIG2g`, `MIG3g`, `MIG4g`, and `MIG7g`. + +> [!IMPORTANT] +> You can't change the applied GPU instance profile after node pool creation. + +## Create an AKS cluster + +1. Create an Azure resource group using the [`az group create`][az-group-create] command. + + ```azurecli-interactive + export RANDOM_ID="$(openssl rand -hex 3)" + export MY_RESOURCE_GROUP_NAME="myAKSResourceGroup$RANDOM_ID" + export REGION="eastus2" + export MY_AKS_CLUSTER_NAME="myAKSCluster$RANDOM_ID" + az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION + ``` + + Results: + + + ```JSON + { + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myAKSResourceGroupxxxxxx", + "location": "eastus", + "managedBy": null, + "name": "testResourceGroup", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" + } + ``` + +2. Create an AKS cluster using the [`az aks create`][az-aks-create] command. + + ```azurecli-interactive + az aks create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_AKS_CLUSTER_NAME\ + --node-count 1 \ + --generate-ssh-keys + ``` + + Results: + + + ```JSON + { + "aadProfile": null, + "addonProfiles": { + "httpApplicationRouting": null, + "kubeDashboard": null, + "omsagent": { + "config": { + "logAnalyticsWorkspaceResourceID": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/xxxxxx/providers/Microsoft.OperationalInsights/workspaces/xxxxxx" + }, + "enabled": false + } + }, + "agentPoolProfiles": [ + { + "availabilityZones": null, + "count": 1, + "enableAutoScaling": false, + "enableEncryptionAtHost": false, + "enableFips": false, + "enableNodePublicIP": false, + "gpuInstanceProfile": null, + "kubeletConfig": null, + "kubeletDiskType": "OS", + "linuxOSConfig": null, + "maxCount": null, + "maxPods": 110, + "minCount": null, + "mode": "System", + "name": "nodepool1", + "nodeImageVersion": "AKSUbuntu-xxxx.x.x.x", + "nodeLabels": null, + "nodePublicIPPrefixID": null, + "nodeTaints": null, + "orchestratorVersion": "x.x.x", + "osDiskSizeGB": 128, + "osDiskType": "Managed", + "osSKU": "Ubuntu", + "osType": "Linux", + "podSubnetID": null, + "powerState": { + "code": "Running" + }, + "provisioningState": "Succeeded", + "proximityPlacementGroupID": null, + "scaleSetEvictionPolicy": null, + "scaleSetPriority": "Regular", + "spotMaxPrice": null, + "tags": null, + "type": "VirtualMachineScaleSets", + "upgradeSettings": { + "maxSurge": null + }, + "vmSize": "Standard_DS2_v2", + "vnetSubnetID": null + } + ], + "apiServerAccessProfile": null, + "autoScalerProfile": null, + "autoUpgradeProfile": null, + "azurePortalFQDN": null, + "azurePortalURL": "https://portal.azure.com/#resource/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/xxxxxx/providers/Microsoft.ContainerService/managedClusters/xxxxxx", + "creationData": null, + "currentKubernetesVersion": "x.x.x", + "diskEncryptionSetID": null, + "dnsPrefix": "xxxxxx", + "enablePodSecurityPolicy": null, + "enableRBAC": true, + "extendedLocation": null, + "fqdn": "xxxxxx-xxxxxx-xxxxxx.hcp.xxxxxx.azmk8s.io", + "fqdnSubdomain": null, + "httpProxyConfig": null, + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/xxxxxx/providers/Microsoft.ContainerService/managedClusters/xxxxxx", + "identity": { + "principalId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "type": "SystemAssigned", + "userAssignedIdentities": null + }, + "identityProfile": null, + "ingressProfile": null, + "keyVaultSecretsProvider": null, + "kubernetesVersion": "x.x.x", + "location": "xxxxxx", + "maxAgentPools": 10, + "monitoringAddonProfile": null, + "name": "xxxxxx", + "networkProfile": { + "dnsServiceIP": "10.0.0.10", + "dockerBridgeCidr": "172.17.0.1/16", + "loadBalancerProfile": { + "allocatedOutboundPorts": null, + "effectiveOutboundIPs": [ + { + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/xxxxxx/providers/Microsoft.Network/publicIPAddresses/xxxxxx", + "resourceGroup": "xxxxxx" + } + ], + "enableMultipleStandardLoadBalancers": null, + "idleTimeoutInMinutes": null, + "managedOutboundIPs": { + "count": 1 + }, + "outboundIPPrefixes": null, + "outboundIPs": null, + "outboundPortsAllocated": null + }, + "loadBalancerSku": "Standard", + "networkMode": null, + "networkPlugin": "kubenet", + "networkPolicy": null, + "outboundType": "loadBalancer", + "podCidr": null, + "serviceCidr": "10.0.0.0/16" + }, + "nodeResourceGroup": "MC_xxxxxx_xxxxxx_xxxxxx", + "oidcIssuerProfile": null, + "podIdentityProfile": null, + "powerState": { + "code": "Running" + }, + "privateFQDN": null, + "privateLinkResources": null, + "provisioningState": "Succeeded", + "publicNetworkAccess": "Enabled", + "resourceGroup": "xxxxxx", + "securityProfile": null, + "servicePrincipalProfile": { + "clientId": "msi" + }, + "sku": { + "name": "Basic", + "tier": "Free" + }, + "storageProfile": { + "blobCsiDriver": { + "enabled": true + }, + "diskCsiDriver": { + "enabled": true + }, + "fileCsiDriver": { + "enabled": true + }, + "snapshotController": { + "enabled": true + } + }, + "tags": null, + "type": "Microsoft.ContainerService/ManagedClusters", + "windowsProfile": null + } + ``` + +## Create a multi-instance GPU node pool + +You can use either the Azure CLI or an HTTP request to the ARM API to create the node pool. + +### [Azure CLI](#tab/azure-cli) + +* Create a multi-instance GPU node pool using the [`az aks nodepool add`][az-aks-nodepool-add] command and specify the GPU instance profile. + + ```azurecli-interactive + export MY_NODE_POOL_NAME="mignode" + az aks nodepool add \ + --name $MY_NODE_POOL_NAME \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --cluster-name $MY_AKS_CLUSTER_NAME \ + --node-vm-size Standard_NC24ads_A100_v4 \ + --gpu-instance-profile MIG1g + ``` + + Results: + + + ```JSON + { + "agentPoolProfile": { + "count": 1, + "enableAutoScaling": false, + "enableEncryptionAtHost": false, + "enableFips": false, + "enableNodePublicIp": false, + "gpuInstanceProfile": "MIG1g", + "kubeletConfig": null, + "linuxOsConfig": null, + "maxCount": null, + "maxPods": 110, + "minCount": null, + "mode": "User", + "name": "mignode", + "nodeImageVersion": "AKSUbuntu-xxxx.x.x.x", + "nodeLabels": {}, + "nodePublicIpPrefixId": null, + "nodeTaints": [], + "orchestratorVersion": "x.x.x", + "osDiskSizeGb": 128, + "osDiskType": "Managed", + "osSku": "Ubuntu", + "osType": "Linux", + "podSubnetId": null, + "provisioningState": "Succeeded", + "proximityPlacementGroupId": null, + "scaleSetEvictionPolicy": null, + "scaleSetPriority": "Regular", + "spotMaxPrice": null, + "tags": null, + "type": "VirtualMachineScaleSets", + "upgradeSettings": { + "maxSurge": "1" + }, + "vmSize": "Standard_NC96ads_A100_v4", + "vnetSubnetId": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/xxxxxx/providers/Microsoft.Network/virtualNetworks/xxxxxx/subnets/xxxxxx" + }, + "creationData": null, + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/xxxxxx/providers/Microsoft.ContainerService/managedClusters/xxxxxx/agentPools/mignode", + "name": "mignode", + "provisioningState": "Succeeded", + "resourceGroup": "xxxxxx", + "type": "Microsoft.ContainerService/managedClusters/agentPools" + } + ``` + +### [HTTP request](#tab/http-request) + +* Create a multi-instance GPU node pool by placing the GPU instance profile in the request body. + + ```http + { + "properties": { + "count": 1, + "vmSize": "Standard_ND96asr_v4", + "type": "VirtualMachineScaleSets", + "gpuInstanceProfile": "MIG1g" + } + } + ``` + +--- + +## Determine multi-instance GPU (MIG) strategy + +Before you install the Nvidia plugins, you need to specify which multi-instance GPU (MIG) strategy to use for GPU partitioning: *Single strategy* or *Mixed strategy*. The two strategies don't affect how you execute CPU workloads, but how GPU resources are displayed. + +* **Single strategy**: The single strategy treats every GPU instance as a GPU. If you use this strategy, the GPU resources are displayed as `nvidia.com/gpu: 1`. +* **Mixed strategy**: The mixed strategy exposes the GPU instances and the GPU instance profile. If you use this strategy, the GPU resource are displayed as `nvidia.com/mig1g.5gb: 1`. + +## Install the NVIDIA device plugin and GPU feature discovery + +1. Set your MIG strategy as an environment variable. You can use either single or mixed strategy. + + ```azurecli-interactive + # Single strategy + export MIG_STRATEGY=single + + # Mixed strategy + export MIG_STRATEGY=mixed + ``` + +2. Add the Nvidia device plugin and GPU feature discovery helm repos using the `helm repo add` and `helm repo update` commands. + + ```azurecli-interactive + helm repo add nvdp https://nvidia.github.io/k8s-device-plugin + helm repo add nvgfd https://nvidia.github.io/gpu-feature-discovery + helm repo update + ``` + +3. Install the Nvidia device plugin using the `helm install` command. + + ```azurecli-interactive + helm install \ + --version=0.14.0 \ + --generate-name \ + --set migStrategy=${MIG_STRATEGY} \ + nvdp/nvidia-device-plugin + ``` + +4. Install the GPU feature discovery using the `helm install` command. + + ```azurecli-interactive + helm install \ + --version=0.2.0 \ + --generate-name \ + --set migStrategy=${MIG_STRATEGY} \ + nvgfd/gpu-feature-discovery + ``` + +## Confirm multi-instance GPU capability + +1. Configure `kubectl` to connect to your AKS cluster using the [`az aks get-credentials`][az-aks-get-credentials] command. + + ```azurecli-interactive + az aks get-credentials --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_AKS_CLUSTER_NAME + ``` + +2. Verify the connection to your cluster using the `kubectl get` command to return a list of cluster nodes. + + ```azurecli-interactive + kubectl get nodes -o wide + ``` + +3. Confirm the node has multi-instance GPU capability using the `kubectl describe node` command. The following example command describes the node named *mignode*, which uses MIG1g as the GPU instance profile. + + ```azurecli-interactive + kubectl describe node mignode + ``` + + Your output should resemble the following example output: + + ```output + # Single strategy output + Allocatable: + nvidia.com/gpu: 56 + + # Mixed strategy output + Allocatable: + nvidia.com/mig-1g.5gb: 56 + ``` + +## Schedule work + +The following examples are based on cuda base image version 12.1.1 for Ubuntu22.04, tagged as `12.1.1-base-ubuntu22.04`. + +### Single strategy + +1. Create a file named `single-strategy-example.yaml` and copy in the following manifest. + + ```bash + cat < single-strategy-example.yaml + apiVersion: v1 + kind: Pod + metadata: + name: nvidia-single + spec: + containers: + - name: nvidia-single + image: nvidia/cuda:12.1.1-base-ubuntu22.04 + command: ["/bin/sh"] + args: ["-c","sleep 1000"] + resources: + limits: + "nvidia.com/gpu": 1 + EOF + ``` + +2. Deploy the application using the `kubectl apply` command and specify the name of your YAML manifest. + + ```azurecli-interactive + kubectl apply -f single-strategy-example.yaml + ``` + +3. Verify the allocated GPU devices using the `kubectl exec` command. This command returns a list of the cluster nodes. + + ```azurecli-interactive + kubectl exec nvidia-single -- nvidia-smi -L + ``` + + The following example resembles output showing successfully created deployments and services: + + ```output + GPU 0: NVIDIA A100 40GB PCIe (UUID: GPU-48aeb943-9458-4282-da24-e5f49e0db44b) + MIG 1g.5gb Device 0: (UUID: MIG-fb42055e-9e53-5764-9278-438605a3014c) + MIG 1g.5gb Device 1: (UUID: MIG-3d4db13e-c42d-5555-98f4-8b50389791bc) + MIG 1g.5gb Device 2: (UUID: MIG-de819d17-9382-56a2-b9ca-aec36c88014f) + MIG 1g.5gb Device 3: (UUID: MIG-50ab4b32-92db-5567-bf6d-fac646fe29f2) + MIG 1g.5gb Device 4: (UUID: MIG-7b6b1b6e-5101-58a4-b5f5-21563789e62e) + MIG 1g.5gb Device 5: (UUID: MIG-14549027-dd49-5cc0-bca4-55e67011bd85) + MIG 1g.5gb Device 6: (UUID: MIG-37e055e8-8890-567f-a646-ebf9fde3ce7a) + ``` + +### Mixed strategy + +1. Create a file named `mixed-strategy-example.yaml` and copy in the following manifest. + + ```yaml + cat < mixed-strategy-example.yaml + apiVersion: v1 + kind: Pod + metadata: + name: nvidia-mixed + spec: + containers: + - name: nvidia-mixed + image: nvidia/cuda:12.1.1-base-ubuntu22.04 + command: ["/bin/sh"] + args: ["-c","sleep 100"] + resources: + limits: + "nvidia.com/mig-1g.5gb": 1 + EOF + ``` + +2. Deploy the application using the `kubectl apply` command and specify the name of your YAML manifest. + + ```azurecli-interactive + kubectl apply -f mixed-strategy-example.yaml + ``` + +3. Verify the allocated GPU devices using the `kubectl exec` command. This command returns a list of the cluster nodes. + + ```azurecli-interactive + kubectl exec nvidia-mixed -- nvidia-smi -L + ``` + + The following example resembles output showing successfully created deployments and services: + + ```output + GPU 0: NVIDIA A100 40GB PCIe (UUID: GPU-48aeb943-9458-4282-da24-e5f49e0db44b) + MIG 1g.5gb Device 0: (UUID: MIG-fb42055e-9e53-5764-9278-438605a3014c) + ``` + +> [!IMPORTANT] +> The `latest` tag for CUDA images has been deprecated on Docker Hub. Please refer to [NVIDIA's repository](https://hub.docker.com/r/nvidia/cuda/tags) for the latest images and corresponding tags. + +## Troubleshooting + +If you don't see multi-instance GPU capability after creating the node pool, confirm the API version isn't older than *2021-08-01*. + +## Next steps + +For more information on AKS node pools, see [Manage node pools for a cluster in AKS](./manage-node-pools.md). + + +[az-group-create]: /cli/azure/group#az_group_create +[az-aks-create]: /cli/azure/aks#az_aks_create +[az-aks-nodepool-add]: /cli/azure/aks/nodepool#az_aks_nodepool_add +[install-azure-cli]: /cli/azure/install-azure-cli +[az-aks-install-cli]: /cli/azure/aks#az_aks_install_cli +[az-aks-get-credentials]: /cli/azure/aks#az_aks_get_credentials + + +[Nvidia A100 GPU]:https://www.nvidia.com/en-us/data-center/a100/ \ No newline at end of file diff --git a/scenarios/ObtainPerformanceMetricsLinuxSustem/obtain-performance-metrics-linux-system.md b/scenarios/ObtainPerformanceMetricsLinuxSustem/obtain-performance-metrics-linux-system.md new file mode 100644 index 000000000..7e8499928 --- /dev/null +++ b/scenarios/ObtainPerformanceMetricsLinuxSustem/obtain-performance-metrics-linux-system.md @@ -0,0 +1,638 @@ +--- +title: Obtaining Performance metrics from a Linux system +description: Learn how to obtainer Performance metrics from a Linux system. +author: divargas-msft +ms.author: esflores +editor: divargas-msft +ms.reviewer: divargas +ms.service: virtual-machines +ms.collection: linux +ms.topic: troubleshooting-general +ms.workload: infrastructure-services +ms.tgt_pltfrm: vm-linux +ms.date: 07/16/2024 +ms.custom: devx-track-azurecli, mode-api, innovation-engine, linux-related-content +--- + +# Obtaining Performance metrics from a Linux system + +**Applies to:** :heavy_check_mark: Linux VMs + +This article is going to cover instructions to determine how to quickly obtain performance metrics from a Linux System. + +There are several commands that can be used to obtain performance counters on Linux. Commands such as `vmstat` and `uptime`, provide general system metrics such as CPU usage, System Memory, and System load. +Most of the commands are already installed by default with others being readily available in default repositories. +The commands can be separated into: + +* CPU +* Memory +* Disk I/O +* Processes + +## Sysstat utilities installation + + + +> [!NOTE] +> Some of these commands need to be run as `root` to be able to gather all relevant details. + +> [!NOTE] +> Some commands are part of the `sysstat` package which might not be installed by default. The package can be easily installed with `sudo apt install sysstat`, `dnf install sysstat` or `zypper install sysstat` for those popular distros. + +The full command for installation of the `sysstat` package on some popular Distros is: + +```bash +az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts "/bin/bash -c 'OS=\$(cat /etc/os-release|grep NAME|head -1|cut -d= -f2 | sed \"s/\\\"//g\"); if [[ \$OS =~ \"Ubuntu\" ]] || [[ \$OS =~ \"Debian\" ]]; then sudo apt install sysstat -y; elif [[ \$OS =~ \"Red Hat\" ]]; then sudo dnf install sysstat -y; elif [[ \$OS =~ \"SUSE\" ]]; then sudo zypper install sysstat --non-interactive; else echo \"Unknown distribution\"; fi'" +``` + +## CPU + +### mpstat + +The `mpstat` utility is part of the `sysstat` package. It displays per CPU utilization and averages, which is helpful to quickly identify CPU usage. `mpstat` provides an overview of CPU utilization across the available CPUs, helping identify usage balance and if a single CPU is heavily loaded. + +The full command is: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'mpstat -P ALL 1 2') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +The options and arguments are: + +* `-P`: Indicates the processor to display statistics, the ALL argument indicates to display statistics for all the online CPUs in the system. +* `1`: The first numeric argument indicates how often to refresh the display in seconds. +* `2`: The second numeric argument indicates how many times the data refreshes. + +The number of times the `mpstat` command displays data can be changed by increasing the second numeric argument to accommodate for longer data collection times. Ideally 3 or 5 seconds should suffice, for systems with increased core counts 2 seconds can be used to reduce the amount of data displayed. +From the output: + +```output +Linux 5.14.0-362.8.1.el9_3.x86_64 (alma9) 02/21/24 _x86_64_ (8 CPU) + +16:55:50 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle +16:55:51 all 69.09 0.00 30.16 0.00 0.38 0.38 0.00 0.00 0.00 0.00 +16:55:51 0 77.23 0.00 21.78 0.00 0.99 0.00 0.00 0.00 0.00 0.00 +16:55:51 1 97.03 0.00 0.99 0.00 0.99 0.99 0.00 0.00 0.00 0.00 +16:55:51 2 11.11 0.00 88.89 0.00 0.00 0.00 0.00 0.00 0.00 0.00 +16:55:51 3 11.00 0.00 88.00 0.00 0.00 1.00 0.00 0.00 0.00 0.00 +16:55:51 4 83.84 0.00 16.16 0.00 0.00 0.00 0.00 0.00 0.00 0.00 +16:55:51 5 76.00 0.00 23.00 0.00 1.00 0.00 0.00 0.00 0.00 0.00 +16:55:51 6 96.00 0.00 3.00 0.00 0.00 1.00 0.00 0.00 0.00 0.00 +16:55:51 7 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 +[...] + +Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle +Average: all 74.02 0.00 25.52 0.00 0.25 0.21 0.00 0.00 0.00 0.00 +Average: 0 63.00 0.00 36.67 0.00 0.33 0.00 0.00 0.00 0.00 0.00 +Average: 1 97.33 0.00 1.67 0.00 0.33 0.67 0.00 0.00 0.00 0.00 +Average: 2 42.33 0.00 57.33 0.00 0.33 0.00 0.00 0.00 0.00 0.00 +Average: 3 34.33 0.00 65.00 0.00 0.33 0.33 0.00 0.00 0.00 0.00 +Average: 4 88.63 0.00 11.04 0.00 0.00 0.33 0.00 0.00 0.00 0.00 +Average: 5 71.33 0.00 28.33 0.00 0.33 0.00 0.00 0.00 0.00 0.00 +Average: 6 95.65 0.00 4.01 0.00 0.00 0.33 0.00 0.00 0.00 0.00 +Average: 7 99.67 0.00 0.00 0.00 0.33 0.00 0.00 0.00 0.00 0.00 +``` + +There are a couple of important things to note. The first line displays useful information: + +* Kernel and release: `5.14.0-362.8.1.el9_3.x86_64` +* Hostname: `alma9` +* Date: `02/21/24` +* Architecture: `_x86_64_` +* Total amount of CPUs (this information is useful to interpret the output from other commands): `(8 CPU)` + +Then the metrics for the CPUs are displayed, to explain each of the columns: + +* `Time`: The time the sample was collected +* `CPU`: The CPU numeric identifier, the ALL identifier is an average for all the CPUs. +* `%usr`: The percentage of CPU utilization for user space, normally user applications. +* `%nice`: The percentage of CPU utilization for user space processes with a nice (priority) value. +* `%sys`: The percentage of CPU utilization for kernel space processes. +* `%iowait`: The percentage of CPU time spent idle waiting for outstanding I/O. +* `%irq`: The percentage of CPU time spent serving hardware interrupts. +* `%soft`: The percentage of CPU time spent serving software interrupts. +* `%steal`: The percentage of CPU time spent serving other virtual machines (not applicable to Azure due to no overprovisioning of CPU). +* `%guest`: The percentage of CPU time spent serving virtual CPUs (not applicable to Azure, only applicable to bare metal systems running virtual machines). +* `%gnice`: The percentage of CPU time spent serving virtual CPUs with a nice value (not applicable to Azure, only applicable to bare metal systems running virtual machines). +* `%idle`: The percentage of CPU time spent idle, and without waiting for I/O requests. + +#### Things to look out for + +Some details to keep in mind when reviewing the output for `mpstat`: + +* Verify that all CPUs are properly loaded and not a single CPU is serving all the load. This information could indicate a single threaded application. +* Look for a healthy balance between `%usr` and `%sys` as the opposite would indicate more time spent on the actual workload than serving kernel processes. +* Look for `%iowait` percentages as high values could indicate a system that is constantly waiting for I/O requests. +* High `%soft` usage could indicate high network traffic. + +### `vmstat` + +The `vmstat` utility is widely available in most Linux distributions, it provides high level overview for CPU, Memory, and Disk I/O utilization in a single pane. +The command for `vmstat` is: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'vmstat -w 1 5') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +The options and arguments are: + +* `-w`: Use wide printing to keep consistent columns. +* `1`: The first numeric argument indicates how often to refresh the display in seconds. +* `5`: The second numeric argument indicates how many times the data refreshes. + +The output: + +```output +--procs-- -----------------------memory---------------------- ---swap-- -----io---- -system-- --------cpu-------- + r b swpd free buff cache si so bi bo in cs us sy id wa st + 14 0 0 26059408 164 137468 0 0 89 3228 56 122 3 1 95 1 0 + 14 1 0 24388660 164 145468 0 0 0 7811 3264 13870 76 24 0 0 0 + 18 1 0 23060116 164 155272 0 0 44 8075 3704 15129 78 22 0 0 0 + 18 1 0 21078640 164 165108 0 0 295 8837 3742 15529 73 27 0 0 0 + 15 2 0 19015276 164 175960 0 0 9 8561 3639 15177 73 27 0 0 0 +``` + +`vmstat` splits the output in six groups: + +* `procs`: statistics for processes. +* `memory`: statistics for system memory. +* `swap`: statistics for swap. +* `io`: statistics for disk io. +* `system`: statistics for context switches and interrupts. +* `cpu`: statistics for CPU usage. + +>Note: `vmstat` shows overall statistics for the entire system (that is, all CPUs, all block devices aggregated). + +#### `procs` + +The `procs` section has two columns: + +* `r`: The number of runnable processes in the run queue. +* `b`: The number of processes blocked waiting for I/O. + +This section immediately shows if there's any bottleneck on the system. High numbers on either of the columns indicate processes queuing up waiting for resources. + +The `r` column indicates the number of processes that are waiting for CPU time to be able to run. An easy way to interpret this number is as follows: if the number of processes in the `r` queue is higher than the number of total CPUs, then it can be inferred that the system has the CPU heavily loaded, and it can't allocate CPU time for all the processes waiting to run. + +The `b` column indicates the number of processes waiting to run that are being blocked by I/O requests. A high number in this column would indicate a system that's experiencing high I/O, and processes are unable to run due to other processes waiting to completed I/O requests. Which could also indicate high disk latency. + +#### `memory` + +The memory section has four columns: + +* `swpd`: The amount swap memory used. +* `free`: The amount of memory free. +* `buff`: The amount of memory used for buffers. +* `cache`: The amount of memory used for cache. + +> [!NOTE] +> The values are shown in bytes. + +This section provides a high level overview of memory usage. + +#### `swap` + +The swap section has two columns: + +* `si`: The amount of memory swapped in (moved from system memory to swap) per second. +* `so`: The amount of memory swapped out (moved from swap to system memory) per second. + +If high `si` is observed, it might represent a system that is running out of system memory and is moving pages to swap (swapping). + +#### `io` + +The `io` section has two columns: + +* `bi`: The number of blocks received from a block device (reads blocks per second) per second. +* `bo`: The number of blocks sent to a block device (writes per second) per second. + +> [!NOTE] +> These values are in blocks per second. + +#### `system` + +The `system` section has two columns: + +* `in`: The number of interrupts per second. +* `cs`: The number of context switches per second. + +A high number of interrupts per second might indicate a system that is busy with hardware devices (for example network operations). + +A high number of context switches might indicate a busy system with many short running processes, there's no good or bad number here. + +#### `cpu` + +This section has five columns: + +* `us`: User space percent utilization. +* `sy`: System (kernel space) percent utilization. +* `id`: Percent utilization of the amount of time the CPU is idle. +* `wa`: Percent utilization of the amount of time the CPU is idle waiting for processes with I/O. +* `st`: Percent utilization of the amount of time the CPU spent serving other virtual CPUs (not applicable to Azure). + +The values are presented in percentage. These values are the same as presented by the `mpstat` utility and serve to provide a high level overview of CPU usage. Follow a similar process for "[Things to look out for](#mpstat)" for `mpstat` when reviewing these values. + +### `uptime` + +Lastly, for CPU related metrics, the `uptime` utility provides a broad overview of the system load with the load average values. + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'uptime') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +```output +16:55:53 up 9 min, 2 users, load average: 9.26, 2.91, 1.18 +``` + +The load average displays three numbers. These numbers are for `1`, `5` and `15` minute intervals of system load. + +To interpret these values, it's important to know the number of available CPUs in the system, obtained from the `mpstat` output before. The value depends on the total CPUs, so as an example of the `mpstat` output the system has 8 CPUs, a load average of 8 would mean that ALL cores are loaded to a 100%. + +A value of `4` would mean that half of the CPUs were loaded at 100% (or a total of 50% load on ALL CPUs). In the previous output, the load average is `9.26`, which means the CPU is loaded at about 115%. + +The `1m`, `5m`, `15m` intervals help identify if load is increasing or decreasing over time. + +> [NOTE] +> The `nproc` command can also be used to obtain the number of CPUs. + +## Memory + +For memory, there are two commands that can obtain details about usage. + +### `free` + +The `free` command shows system memory utilization. + +To run it: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'free -h') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +The options and arguments are: + +* `-h`: Display values dynamically as human readable (for example: Mib, Gib, Tib) + +The output: + +```output + total used free shared buff/cache available +Mem: 31Gi 19Gi 12Gi 23Mi 87Mi 11Gi +Swap: 23Gi 0B 23Gi +``` + +From the output, look for the total system memory vs the available, and the used vs total swap. The available memory takes into consideration memory allocated for cache, which can be returned for user applications. + +Some swap usage is normal in modern kernels as some less often used memory pages can be moved to swap. + +### `swapon` + +The `swapon` command displays where swap is configured and the respective priorities of the swap devices or files. + +To run the command: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'swapon -s') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +The output: + +```output +Filename Type Size Used Priority +/dev/zram0 partition 16G 0B 100 +/mnt/swapfile file 8G 0B -2 +``` + +This information is important to verify if swap is configured on a location that isn't ideal, for example on a data or OS disk. In the Azure frame of reference, swap should be configured on the ephemeral drive as it provides the best performance. + +### Things to look out for + +* Keep in mind the memory is a finite resource, once both system memory (RAM) and swap is exhausted, the processes are to be killed by the Out Of Memorry killer (OOM). +* Verify swap isn't configured on a data disk or the OS disk, as that would create issues with I/O due to latency differences. Swap should be configured on the ephemeral drive. +* Keep also in consideration that it's common to see on the `free -h` output that the free values are close to zero, this behavior is due to page cache, the kernel releases those pages as needed. + +## I/O + +Disk I/O is one of the areas Azure suffers the most when throttled, as disks can reach `100ms+` latencies. The following commands help to identify these scenarios. + +### `iostat` + +The `iostat` utility is part of the `sysstat` package. It displays per block device usage statistics and helps identify block related performance issues. + +The `iostat` utility provides details for metrics such as throughput, latency, and queue size. These metrics help understand if disk I/O becomes a limiting factor. +To run, use the command: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'iostat -dxtm 1 5') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +The options and arguments are: + +* `-d`: Per device usage report. +* `-x`: Extended statistics. +* `-t`: Display the timestamp for each report. +* `-m`: Display in MB/s. +* `1`: The first numeric argument indicates how often to refresh the display in seconds. +* `2`: The second numeric argument indicates how many times the data refreshes. + +The output: + +```output +Linux 5.14.0-362.8.1.el9_3.x86_64 (alma9) 02/21/24 _x86_64_ (8 CPU) + +02/21/24 16:55:50 +Device r/s rMB/s rrqm/s %rrqm r_await rareq-sz w/s wMB/s wrqm/s %wrqm w_await wareq-sz d/s dMB/s drqm/s %drqm d_await dareq-sz f/s f_await aqu-sz %util +sda 1.07 0.02 0.00 0.00 1.95 20.40 23.25 24.55 3.30 12.42 113.75 1081.06 0.26 537.75 0.26 49.83 0.03 2083250.04 0.00 0.00 2.65 2.42 +sdb 16.99 0.67 0.36 2.05 2.00 40.47 65.26 0.44 1.55 2.32 1.32 6.92 0.00 0.00 0.00 0.00 0.00 0.00 30.56 1.30 0.16 7.16 +zram0 0.51 0.00 0.00 0.00 0.00 4.00 0.00 0.00 0.00 0.00 0.00 4.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + +``` + +The output has several columns that aren't important (extra columns due to the `-x` option), some of the important ones are: + +* `r/s`: Read operations per second (IOPS). +* `rMB/s`: Read megabytes per second. +* `r_await`: Read latency in milliseconds. +* `rareq-sz`: Average read request size in kilobytes. +* `w/s`: Write operations per second (IOPS). +* `wMB/s`: Write megabytes per second. +* `w_await`: Write latency in milliseconds. +* `wareq-size`: Average write request size in kilobytes. +* `aqu-sz`: Average queue size. + +#### Things to look out for + +* Look for `r/s` and `w/s` (IOPS) and `rMB/s` and `wMB/s` and verify that these values are within the limits of the given disk. If the values are close or higher the limits, the disk are going to be throttled, leading to high latency. This information can also be corroborated with the `%iowait` metric from `mpstat`. +* The latency is an excellent metric to verify if the disk is performing as expected. Normally, less than `9ms` is the expected latency for PremiumSSD, other offerings have different latency targets. +* The queue size is a great indicator of saturation. Normally, requests would be served near real time and the number remains close to one (as the queue never grows). A higher number could indicate disk saturation (that is, requests queuing up). There's no good or bad number for this metric. Understanding that anything higher than one means that requests are queuing up helps determine if there's disk saturation. + +### `lsblk` + +The `lsblk` utility shows the block devices attached to the system, while it doesn't provide performance metrics, it allows a quick overview of how these devices are configured and which mountpoints are being used. + +To run, use the command: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'lsblk') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +The output: + +```output +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS +sda 8:0 0 300G 0 disk +└─sda1 8:1 0 300G 0 part /mnt +sdb 8:16 0 30G 0 disk +├─sdb1 8:17 0 1M 0 part +├─sdb2 8:18 0 200M 0 part /boot/efi +├─sdb3 8:19 0 1G 0 part /boot +└─sdb4 8:20 0 28.8G 0 part / +zram0 252:0 0 16G 0 disk [SWAP] +``` + +#### Things to look out for + +* Look for where the devices are mounted. +* Verify swap it's not configured inside of a data disk or OS disk, if enabled. + +> Note: An easy way to correlate the block device to a LUN in Azure is by running `ls -lr /dev/disk/azure`. + +## Process + +Gathering details on a per process basis helps understand where the load of the system is coming from. + +The main utility to gather process statics is `pidstat` as it provides details per process for CPU, Memory, and I/O statistics. + +Lastly, a simple `ps` to sort process by top CPU, and memory usage complete the metrics. + +> [!NOTE] +> Since these commands display details about running processes, they need to run as root with `sudo`. This command allows all processes to be displayed and not just the user's. + +### `pidstat` + +The `pidstat` utility is also part of the `sysstat` package. It's like `mpstat` or iostat where it displays metrics for a given amount of time. By default, `pidstat` only displays metrics for processes with activity. + +Arguments for `pidstat` are the same for other `sysstat` utilities: + +* 1: The first numeric argument indicates how often to refresh the display in seconds. +* 2: The second numeric argument indicates how many times the data refreshes. + +> [!NOTE] +> The output can grow considerably if there are many processes with activity. + +#### Process CPU statistics + +To gather process CPU statistics, run `pidstat` without any options: + +The following commands can be used if you want to execute it from Azure CLI: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'pidstat 1 2') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +The output: + +```output +Linux 5.14.0-362.8.1.el9_3.x86_64 (alma9) 02/21/24 _x86_64_ (8 CPU) + +# Time UID PID %usr %system %guest %wait %CPU CPU Command +16:55:48 0 66 0.0% 1.0% 0.0% 0.0% 1.0% 0 kworker/u16:2-xfs-cil/sdb4 +16:55:48 0 70 0.0% 1.0% 0.0% 0.0% 1.0% 0 kworker/u16:6-xfs-cil/sdb4 +16:55:48 0 92 0.0% 1.0% 0.0% 0.0% 1.0% 3 kworker/3:1H-kblockd +16:55:48 0 308 0.0% 1.0% 0.0% 0.0% 1.0% 1 kworker/1:1H-kblockd +16:55:48 0 2068 0.0% 1.0% 0.0% 0.0% 1.0% 1 kworker/1:3-xfs-conv/sdb4 +16:55:48 0 2181 63.1% 1.0% 0.0% 35.9% 64.1% 5 stress-ng-cpu +16:55:48 0 2182 28.2% 0.0% 0.0% 70.9% 28.2% 6 stress-ng-cpu +16:55:48 0 2183 28.2% 0.0% 0.0% 69.9% 28.2% 7 stress-ng-cpu +16:55:48 0 2184 62.1% 0.0% 0.0% 36.9% 62.1% 0 stress-ng-cpu +16:55:48 0 2185 43.7% 0.0% 0.0% 54.4% 43.7% 2 stress-ng-cpu +16:55:48 0 2186 30.1% 0.0% 0.0% 68.0% 30.1% 7 stress-ng-cpu +16:55:48 0 2187 64.1% 0.0% 0.0% 34.0% 64.1% 3 stress-ng-cpu +``` + +The command displays per process usage for `%usr`, `%system`, `%guest` (not applicable to Azure), `%wait`, and total `%CPU` usage. + +##### Things to look out for + +* Look for processes with high %wait (iowait) percentage as it might indicate processes that are blocked waiting for I/O, which might also indicate disk saturation. +* Verify that no single process consumes 100% of the CPU as it might indicate a single threaded application. + +#### Process Memory statistics + +To gather process memory statistics, use the `-r` option: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'pidstat -r 1 2') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +The output: + +```output +Linux 5.14.0-362.8.1.el9_3.x86_64 (alma9) 02/21/24 _x86_64_ (8 CPU) + +# Time UID PID minflt/s majflt/s VSZ RSS %MEM Command +16:55:49 0 2199 119244.12 0.00 13.6G 7.4G 23.5% stress-ng-vm +16:55:49 0 2200 392911.76 0.00 13.6G 9.3G 29.7% stress-ng-vm +16:55:49 0 2211 1129.41 0.00 72.3M 3.2M 0.0% stress-ng-iomix +16:55:49 0 2220 0.98 0.00 71.8M 2.4M 0.0% stress-ng-iomix +16:55:49 0 2239 1129.41 0.00 72.3M 3.2M 0.0% stress-ng-iomix +16:55:49 0 2240 1129.41 0.00 72.3M 3.2M 0.0% stress-ng-iomix +16:55:49 0 2256 0.98 0.00 71.8M 2.4M 0.0% stress-ng-iomix +16:55:49 0 2265 1129.41 0.00 72.3M 3.2M 0.0% stress-ng-iomix +``` + +The metrics collected are: + +* `minflt/s`: Minor faults per second, this metric indicates the number of pages loaded from system memory (RAM). +* `mjflt/s`: Major faults per second, this metric indicates the number of pages loaded from disk (SWAP). +* `VSZ`: Virtual memory used in bytes. +* `RSS`: Resident memory used (actual allocated memory) in bytes. +* `%MEM`: Percentage of total memory used. +* `Command`: The name of the process. + +##### Things to look out for + +* Look for major faults per second, as this value would indicate a process that is swapping pages to or from disk. This behavior could indicate memory exhaustion, and could lead to `OOM` events or performance degradation due to slower swap. +* Verify that a single process doesn't consume 100% of the available memory. This behavior could indicate a memory leak. + +> [!NOTE] +> the `--human` option can be used to display numbers in human readable format (that is, `Kb`, `Mb`, `GB`). + +#### Process I/O statistics + +To gather process memory statistics, use the `-d` option: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'pidstat -d 1 2') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +The output: + +```outputLinux 5.14.0-362.8.1.el9_3.x86_64 (alma9) 02/21/24 _x86_64_ (8 CPU) + +# Time UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command +16:55:50 0 86 55.4k 0.0B 0.0B 0 kworker/1:1-xfs-conv/sdb4 +16:55:50 0 2201 4.0k 194.1k 0.0B 0 stress-ng-iomix +16:55:50 0 2202 0.0B 99.0k 0.0B 0 stress-ng-iomix +16:55:50 0 2203 0.0B 23.8k 0.0B 0 stress-ng-iomix +16:55:50 0 2204 0.0B 15.8k 0.0B 0 stress-ng-iomix +16:55:50 0 2212 0.0B 103.0k 0.0B 0 stress-ng-iomix +16:55:50 0 2213 4.0k 99.0k 0.0B 0 stress-ng-iomix +16:55:50 0 2215 0.0B 178.2k 0.0B 0 stress-ng-iomix +16:55:50 0 2216 7.9k 237.6k 0.0B 0 stress-ng-iomix +16:55:50 0 2218 0.0B 95.0k 0.0B 0 stress-ng-iomix +16:55:50 0 2221 0.0B 15.8k 0.0B 0 stress-ng-iomix +``` + +The metrics collected are: + +* `kB_rd/s`: Read kilobytes per second. +* `kB_wr/s`: Write kilobytes per second. +* `Command`: Name of the process. + +##### Things to look out for + +* Look for single processes with high read/write rates per second. This information is a guidance for processes with I/O more than identifying issues. +Note: the `--human` option can be used to display numbers in human readable format (that is, `Kb`, `Mb`, `GB`). + +### Top CPU processes + +Lastly `ps` command displays system processes, and can be either sorted by CPU or Memory. + +To sort by CPU and obtain the top 10 processes: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'ps aux --sort=-%cpu | head -10') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +```output +USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND +root 2190 94.8 0.0 73524 5588 pts/1 R+ 16:55 0:14 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +root 2200 56.8 43.1 14248092 14175632 pts/1 R+ 16:55 0:08 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +root 2192 50.6 0.0 73524 5836 pts/1 R+ 16:55 0:07 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +root 2184 50.4 0.0 73524 5836 pts/1 R+ 16:55 0:07 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +root 2182 44.3 0.0 73524 5808 pts/1 R+ 16:55 0:06 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +root 2187 43.4 0.0 73524 5708 pts/1 R+ 16:55 0:06 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +root 2199 42.9 33.0 14248092 10845272 pts/1 R+ 16:55 0:06 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +root 2186 42.0 0.0 73524 5836 pts/1 R+ 16:55 0:06 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +root 2191 41.2 0.0 73524 5592 pts/1 R+ 16:55 0:06 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +``` + +## Top memory processes +To sort by `MEM%` and obtain the top 10 processes: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'ps aux --sort=-%mem| head -10') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +```output + PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND +root 2200 57.0 43.1 14248092 14175632 pts/1 R+ 16:55 0:08 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +root 2199 43.0 33.0 14248092 10871144 pts/1 R+ 16:55 0:06 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +root 1231 0.2 0.1 336308 33764 ? Sl 16:46 0:01 /usr/bin/python3 -u bin/WALinuxAgent-2.9.1.1-py3.8.egg -run-exthandlers +root 835 0.0 0.0 127076 24860 ? Ssl 16:46 0:00 /usr/bin/python3 -s /usr/sbin/firewalld --nofork --nopid +root 1199 0.0 0.0 30164 15600 ? Ss 16:46 0:00 /usr/bin/python3 -u /usr/sbin/waagent -daemon +root 1 0.2 0.0 173208 12356 ? Ss 16:46 0:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 31 +root 966 0.0 0.0 3102460 10936 ? Sl 16:46 0:00 /var/lib/waagent/Microsoft.GuestConfiguration.ConfigurationforLinux-1.26.60/GCAgent/GC/gc_linux_service +panzer 1803 0.0 0.0 22360 8220 ? Ss 16:49 0:00 /usr/lib/systemd/systemd --user +root 2180 0.0 0.0 73524 6968 pts/1 SL+ 16:55 0:00 stress-ng --cpu 12 --vm 2 --vm-bytes 120% --iomix 4 --timeout 240 +``` + +## Putting all together + +A simple bash script can collect all details in a single run, and append the output to a file for later use: + +```azurecli-interactive +output=$(az vm run-command invoke --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_VM_NAME --command-id RunShellScript --scripts 'mpstat -P ALL 1 2 && vmstat -w 1 5 && uptime && free -h && swapon && iostat -dxtm 1 1 && lsblk && ls -l /dev/disk/azure && pidstat 1 1 -h --human && pidstat -r 1 1 -h --human && pidstat -d 1 1 -h --human && ps aux --sort=-%cpu | head -20 && ps aux --sort=-%mem | head -20') +value=$(echo "$output" | jq -r '.value[0].message') +extracted=$(echo "$value" | awk '/\[stdout\]/,/\[stderr\]/' | sed '/\[stdout\]/d' | sed '/\[stderr\]/d') +echo "$extracted" +``` + +To run, you can create a file with the above contents, add execute permissions by running `chmod +x gather.sh`, and run with `sudo ./gather.sh`. + +This script saves the output of the commands in a file located in the same directory where the script was invoked. \ No newline at end of file diff --git a/scenarios/PostgresRagLlmDemo/README.md b/scenarios/PostgresRagLlmDemo/README.md index 9419ccb98..c32d4c412 100644 --- a/scenarios/PostgresRagLlmDemo/README.md +++ b/scenarios/PostgresRagLlmDemo/README.md @@ -11,12 +11,12 @@ ms.custom: innovation-engine, linux-related-content ## Introduction In this doc, we go over how to host the infrastructure required to run a basic LLM model with RAG capabilities on Azure. -We first set up a Postgres database capable of storing vector embeddings for documents/knowledge files that we want to use to -augment our queries. We then create an Azure OpenAI deployment capable of generating embeddings and answering questions using the latest 'gpt-4-turbo' model. -We then use a python script to fill our postgres database with embeddings from a sample "knowledge.txt" file containing information about an imaginary -resource called 'Zytonium'. Once the database is filled with those embeddings, we use the same python script to answer any -questions we have about 'Zytonium'. The script will search the database for relevant information for our query using an embeddings search and -then augment our query with that relevant information before being sent our LLM to answer. + +We first set up a Postgres database capable of storing vector embeddings for documents/knowledge files that we want to use to augment our queries. We then create an Azure OpenAI deployment capable of generating embeddings and answering questions using the latest 'gpt-4-turbo' model. + +We then use a python script to fill our postgres database with embeddings from a sample "knowledge.txt" file containing information about an imaginary resource called 'Zytonium'. Once the database is filled with those embeddings, we use the same python script to answer any questions we have about 'Zytonium'. + +The script will search the database for relevant information for our query using an embeddings search and then augment our query with that relevant information before being sent our LLM to answer. ## Set up resource group @@ -29,7 +29,7 @@ export REGION="centralus" az group create \ --name $RG_NAME \ - --location $REGION \ + --location $REGION ``` ## Create OpenAI resources @@ -46,7 +46,7 @@ az cognitiveservices account create \ --resource-group $RG_NAME \ --location westus \ --kind OpenAI \ - --sku s0 \ + --sku s0 ``` ## Create OpenAI deployments @@ -123,10 +123,8 @@ psql \ ## Populate with data from knowledge file -The chat bot uses a local file called "knowledge.txt" as the sample document to generate embeddings for -and to store those embeddings in the newly created postgres database. Then any questions you ask will -be augmented with context from the "knowledge.txt" after searching the document for the most relevant -pieces of context using the embeddings. The "knowledge.txt" is about a fictional material called Zytonium. +The chat bot uses a local file called "knowledge.txt" as the sample document to generate embeddings for and to store those embeddings in the newly created postgres database. Then any questions you ask will be augmented with context from the "knowledge.txt" after searching the document for the most relevant pieces of context using the embeddings. The "knowledge.txt" is about a fictional material called Zytonium. + You can view the full knowledge.txt and the code for the chatbot by looking in the "scenarios/PostgresRagLlmDemo" directory. ```bash @@ -140,10 +138,10 @@ python chat.py --populate --api-key $API_KEY --endpoint $ENDPOINT --pguser $PGUS ## Run Chat bot -This final step prints out the command you can copy/paste into the terminal to run the chatbot. `cd ~/scenarios/PostgresRagLlmDemo && python chat.py --api-key $API_KEY --endpoint $ENDPOINT --pguser $PGUSER --phhost $PGHOST --pgpassword $PGPASSWORD --pgdatabase $PGDATABASE` +To run the chatbot, paste this following command to the terminal: `cd ~/scenarios/PostgresRagLlmDemo && python chat.py --api-key $API_KEY --endpoint $ENDPOINT --pguser $PGUSER --phhost $PGHOST --pgpassword $PGPASSWORD --pgdatabase $PGDATABASE` ```bash echo " To run the chatbot, see the last step for more info. " -``` +``` \ No newline at end of file diff --git a/scenarios/PostgresRagLlmDemo/__pycache__/db.cpython-310.pyc b/scenarios/PostgresRagLlmDemo/__pycache__/db.cpython-310.pyc new file mode 100644 index 000000000..f6aebc19d Binary files /dev/null and b/scenarios/PostgresRagLlmDemo/__pycache__/db.cpython-310.pyc differ diff --git a/scenarios/PostgresRagLlmDemo/chat.py b/scenarios/PostgresRagLlmDemo/chat.py index bc4450a3f..0b2cbaaa4 100644 --- a/scenarios/PostgresRagLlmDemo/chat.py +++ b/scenarios/PostgresRagLlmDemo/chat.py @@ -1,4 +1,5 @@ import argparse +import logging from textwrap import dedent from langchain_text_splitters import RecursiveCharacterTextSplitter @@ -6,6 +7,9 @@ from db import VectorDatabase +# Configure logging +logging.basicConfig(level=logging.INFO) + parser = argparse.ArgumentParser() parser.add_argument('--api-key', dest='api_key', type=str) parser.add_argument('--endpoint', dest='endpoint', type=str) @@ -33,6 +37,7 @@ def __init__(self): ) def load_file(self, text_file: str): + logging.info(f"Loading file: {text_file}") with open(text_file, encoding="UTF-8") as f: data = f.read() chunks = self.text_splitter.create_documents([data]) @@ -40,9 +45,7 @@ def load_file(self, text_file: str): text = chunk.page_content embedding = self.__create_embedding(text) self.db.save_embedding(i, text, embedding) - - def __create_embedding(self, text: str): - return self.api.embeddings.create(model="text-embedding-ada-002", input=text).data[0].embedding + logging.info("Done loading data.") def get_answer(self, question: str): question_embedding = self.__create_embedding(question) @@ -71,22 +74,22 @@ def get_answer(self, question: str): ) return response.choices[0].message.content + def __create_embedding(self, text: str): + return self.api.embeddings.create(model="text-embedding-ada-002", input=text).data[0].embedding + def main(): chat_bot = ChatBot() if args.populate: - print("Loading embedding data into database...") chat_bot.load_file("knowledge.txt") - print("Done loading data.") - return - - while True: - q = input("Ask a question (q to exit): ") - if q == "q": - break - print(chat_bot.get_answer(q)) + else: + while True: + q = input("Ask a question (q to exit): ") + if q == "q": + break + print(chat_bot.get_answer(q)) if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/scenarios/PostgresRagLlmDemo/requirements.txt b/scenarios/PostgresRagLlmDemo/requirements.txt index 9b2c99cd9..c640a75ec 100644 --- a/scenarios/PostgresRagLlmDemo/requirements.txt +++ b/scenarios/PostgresRagLlmDemo/requirements.txt @@ -1,4 +1,4 @@ azure-identity==1.17.1 -openai==1.42.0 +openai==1.55.3 psycopg2==2.9.9 -langchain-text-splitters==0.2.2 +langchain-text-splitters==0.2.2 \ No newline at end of file diff --git a/scenarios/README.md b/scenarios/README.md deleted file mode 100644 index 970ea482a..000000000 --- a/scenarios/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This is a test -This is a test diff --git a/scenarios/SpringBoot/spring-boot.md b/scenarios/SpringBoot/spring-boot.md new file mode 100644 index 000000000..688514935 --- /dev/null +++ b/scenarios/SpringBoot/spring-boot.md @@ -0,0 +1,87 @@ +# SpringBootDemo + +Spring Boot application that we will deploy to Kubernetes clusters in Azure. + +## Deploying to VM + +### Create and connect to the VM + +Log in and create VM: + +```bash +export RANDOM_ID="$(openssl rand -hex 3)" +export RESOURCE_GROUP="SpringBoot$RANDOM_ID" +export REGION="westus2" + +az group create --name ${RESOURCE_GROUP} --location ${REGION} +``` + +```bash +export VM_NAME="springboot-vm$RANDOM_ID" +export ADMIN_USERNAME="vm-admin-name$RANDOM_ID" +export VM_IMAGE="Ubuntu2204" + +az vm create \ + --resource-group ${RESOURCE_GROUP} \ + --name ${VM_NAME} \ + --image ${VM_IMAGE} \ + --admin-username ${ADMIN_USERNAME} \ + --generate-ssh-keys \ + --public-ip-sku Standard --size standard_d4s_v3 +``` + +Store the VM IP address for later: + +```bash +export VM_IP_ADDRESS=`az vm show -d -g ${RESOURCE_GROUP} -n ${VM_NAME} --query publicIps -o tsv` +``` + +Run the following to open port 8080 on the vm since SpringBoot uses it + +```bash +az vm open-port --port 8080 --resource-group ${RESOURCE_GROUP} --name ${VM_NAME} --priority 1100 +``` + +Connect to the VM: + +```bash +ssh -o StrictHostKeyChecking=no -t ${ADMIN_USERNAME}@${VM_IP_ADDRESS} +``` + +### Deploy the application + +Install Java and maven needed for application + +```bash +sudo apt-get update +sudo apt-get install default-jdk +sudo apt-get install maven +``` + +Now it's time to clone the project into the vm and give it proper permissions: + +```bash +cd /opt +sudo git clone https://github.com/dasha91/SpringBootDemo +cd SpringBootDemo +sudo chmod -R 777 /opt/SpringBootDemo/ +``` + +Run and deploy the app + +```bash +mvn clean install +mvn spring-boot:run +``` + +### Verify the application + +Finally, go to http://[$VM_IP_ADDRESS]:8080 to confirm that it's working :D :D :D + +To verify if the application is running, you can use the `curl` command: + +```bash +curl http://[$VM_IP_ADDRESS]:8080 +``` + +If the application is running, you should see the HTML content of the Spring Boot application's home page. \ No newline at end of file diff --git a/scenarios/azure-aks-docs/articles/aks/airflow-create-infrastructure.md b/scenarios/azure-aks-docs/articles/aks/airflow-create-infrastructure.md new file mode 100644 index 000000000..7953b8bf8 --- /dev/null +++ b/scenarios/azure-aks-docs/articles/aks/airflow-create-infrastructure.md @@ -0,0 +1,240 @@ +--- +title: Create the infrastructure for deploying Apache Airflow on Azure Kubernetes Service (AKS) +description: In this article, you create the infrastructure needed to deploy Apache Airflow on Azure Kubernetes Service (AKS) using Helm. +ms.topic: how-to +ms.custom: azure-kubernetes-service +ms.date: 12/19/2024 +author: schaffererin +ms.author: schaffererin +--- + +# Create the infrastructure for running Apache Airflow on Azure Kubernetes Service (AKS) + +In this article, you create the infrastructure required to run Apache Airflow on Azure Kubernetes Service (AKS). + +## Prerequisites + +* If you haven't already, review the [Overview for deploying an Apache Airflow cluster on Azure Kubernetes Service (AKS)](./airflow-overview.md). +* An Azure subscription. If you don't have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). +* Azure CLI version 2.61.0. To install or upgrade, see [Install Azure CLI](/cli/azure/install-azure-cli). +* Helm version 3 or later. To install, see [Installing Helm](https://helm.sh/docs/intro/install/). +* `kubectl`, which is installed in Azure Cloud Shell by default. +* GitHub Repo to store Airflow Dags. +* Docker installed on your local machine. To install, see [Get Docker](https://docs.docker.com/get-docker/). + +## Set environment variables + +* Set the required environment variables for use throughout this guide: + + ```bash + export random=$(echo $RANDOM | tr '[0-9]' '[a-z]') + export MY_LOCATION=canadacentral + export MY_RESOURCE_GROUP_NAME=apache-airflow-rg$(echo $random) + export MY_IDENTITY_NAME=airflow-identity-123$(echo $random) + export MY_ACR_REGISTRY=mydnsrandomname$(echo $random) + export MY_KEYVAULT_NAME=airflow-vault-$(echo $random)-kv + export MY_CLUSTER_NAME=apache-airflow-aks$(echo $random) + export SERVICE_ACCOUNT_NAME=airflow$(echo $random) + export SERVICE_ACCOUNT_NAMESPACE=airflow + export AKS_AIRFLOW_NAMESPACE=airflow + export AKS_AIRFLOW_CLUSTER_NAME=cluster-aks-airflow$(echo $random) + export AKS_AIRFLOW_LOGS_STORAGE_ACCOUNT_NAME=airflowsasa$(echo $random) + export AKS_AIRFLOW_LOGS_STORAGE_CONTAINER_NAME=airflow-logs$(echo $random) + export AKS_AIRFLOW_LOGS_STORAGE_SECRET_NAME=storage-account-credentials$(echo $random) + ``` + +## Create a resource group + +* Create a resource group using the [`az group create`](/cli/azure/group#az-group-create) command. + + ```azurecli-interactive + az group create --name $MY_RESOURCE_GROUP_NAME --location $MY_LOCATION --output table + ``` + + Example output: + + ```output + Location Name + ------------- ----------------- + $MY_LOCATION $MY_RESOURCE_GROUP_NAME + ``` + +## Create an identity to access secrets in Azure Key Vault + +In this step, we create a user-assigned managed identity that the External Secrets Operator uses to access the Airflow passwords stored in Azure Key Vault. + +* Create a user-assigned managed identity using the [`az identity create`](/cli/azure/identity#az-identity-create) command. + + ```azurecli-interactive + az identity create --name $MY_IDENTITY_NAME --resource-group $MY_RESOURCE_GROUP_NAME --output table + export MY_IDENTITY_NAME_ID=$(az identity show --name $MY_IDENTITY_NAME --resource-group $MY_RESOURCE_GROUP_NAME --query id --output tsv) + export MY_IDENTITY_NAME_PRINCIPAL_ID=$(az identity show --name $MY_IDENTITY_NAME --resource-group $MY_RESOURCE_GROUP_NAME --query principalId --output tsv) + export MY_IDENTITY_NAME_CLIENT_ID=$(az identity show --name $MY_IDENTITY_NAME --resource-group $MY_RESOURCE_GROUP_NAME --query clientId --output tsv) + ``` + + Example output: + + ```output + ClientId Location Name PrincipalId ResourceGroup TenantId + ------------------------------------ ------------- -------------------- ------------------------------------ ----------------------- ------------------------------------ + 00001111-aaaa-2222-bbbb-3333cccc4444 $MY_LOCATION $MY_IDENTITY_NAME aaaaaaaa-bbbb-cccc-1111-222222222222 $MY_RESOURCE_GROUP_NAME aaaabbbb-0000-cccc-1111-dddd2222eeee + ``` + +## Create an Azure Key Vault instance + +* Create an Azure Key Vault instance using the [`az keyvault create`](/cli/azure/keyvault#az-keyvault-create) command. + + ```azurecli-interactive + az keyvault create --name $MY_KEYVAULT_NAME --resource-group $MY_RESOURCE_GROUP_NAME --location $MY_LOCATION --enable-rbac-authorization false --output table + export KEYVAULTID=$(az keyvault show --name $MY_KEYVAULT_NAME --query "id" --output tsv) + export KEYVAULTURL=$(az keyvault show --name $MY_KEYVAULT_NAME --query "properties.vaultUri" --output tsv) + ``` + + Example output: + + ```output + Location Name ResourceGroup + ------------- -------------------- ---------------------- + $MY_LOCATION $MY_KEYVAULT_NAME $MY_RESOURCE_GROUP_NAME + ``` + +## Create an Azure Container Registry + +* Create an Azure Container Registry to store and manage your container images using the [`az acr create`](/cli/azure/acr#az-acr-create) command. + + ```azurecli-interactive + az acr create \ + --name ${MY_ACR_REGISTRY} \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --sku Premium \ + --location $MY_LOCATION \ + --admin-enabled true \ + --output table + export MY_ACR_REGISTRY_ID=$(az acr show --name $MY_ACR_REGISTRY --resource-group $MY_RESOURCE_GROUP_NAME --query id --output tsv) + ``` + + Example output: + + ```output + NAME RESOURCE GROUP LOCATION SKU LOGIN SERVER CREATION DATE ADMIN ENABLED + -------------------- ---------------------- ------------- ------- ------------------------------- -------------------- --------------- + mydnsrandomnamebfbje $MY_RESOURCE_GROUP_NAME $MY_LOCATION Premium mydnsrandomnamebfbje.azurecr.io 2024-11-07T00:32:48Z True + ``` + +## Create an Azure storage account + +* Create an Azure Storage Account to store the Airflow logs using the [`az acr create`](/cli/azure/storage/account#az-storage-account-create) command. + + ```azurecli-interactive + az storage account create --name $AKS_AIRFLOW_LOGS_STORAGE_ACCOUNT_NAME --resource-group $MY_RESOURCE_GROUP_NAME --location $MY_LOCATION --sku Standard_ZRS --output table + export AKS_AIRFLOW_LOGS_STORAGE_ACCOUNT_KEY=$(az storage account keys list --account-name $AKS_AIRFLOW_LOGS_STORAGE_ACCOUNT_NAME --query "[0].value" -o tsv) + az storage container create --name $AKS_AIRFLOW_LOGS_STORAGE_CONTAINER_NAME --account-name $AKS_AIRFLOW_LOGS_STORAGE_ACCOUNT_NAME --output table --account-key $AKS_AIRFLOW_LOGS_STORAGE_ACCOUNT_KEY + az keyvault secret set --vault-name $MY_KEYVAULT_NAME --name AKS-AIRFLOW-LOGS-STORAGE-ACCOUNT-NAME --value $AKS_AIRFLOW_LOGS_STORAGE_ACCOUNT_NAME + az keyvault secret set --vault-name $MY_KEYVAULT_NAME --name AKS-AIRFLOW-LOGS-STORAGE-ACCOUNT-KEY --value $AKS_AIRFLOW_LOGS_STORAGE_ACCOUNT_KEY + ``` + + Example output: + + ```output + AccessTier AllowBlobPublicAccess AllowCrossTenantReplication CreationTime EnableHttpsTrafficOnly Kind Location MinimumTlsVersion Name PrimaryLocation ProvisioningState ResourceGroup StatusOfPrimary + ------------ ----------------------- ----------------------------- -------------------------------- ------------------------ --------- ------------- ------------------- ---------------- ----------------- ------------------- ----------------- ----------------- + Hot False False 2024-11-07T00:22:13.323104+00:00 True StorageV2 $MY_LOCATION TLS1_0 airflowsasabfbje $MY_LOCATION Succeeded $MY_RESOURCE_GROUP_NAME available + Created + --------- + True + ``` + +## Create an AKS cluster + +In this step, we create an AKS cluster with workload identity and OIDC issuer enabled. The workload identity gives the External Secrets Operator service account permission to access the Airflow passwords stored in your key vault. + +1. Create an AKS cluster using the [`az aks create`](/cli/azure/aks#az-aks-create) command. + + ```azurecli-interactive + az aks create \ + --location $MY_LOCATION \ + --name $MY_CLUSTER_NAME \ + --tier standard \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --network-plugin azure \ + --node-vm-size Standard_DS4_v2 \ + --node-count 3 \ + --auto-upgrade-channel stable \ + --node-os-upgrade-channel NodeImage \ + --attach-acr ${MY_ACR_REGISTRY} \ + --enable-oidc-issuer \ + --enable-blob-driver \ + --enable-workload-identity \ + --zones 1 2 3 \ + --generate-ssh-keys \ + --output table + ``` + + Example output: + + ```output + AzurePortalFqdn CurrentKubernetesVersion DisableLocalAccounts DnsPrefix EnableRbac Fqdn KubernetesVersion Location MaxAgentPools Name NodeResourceGroup ProvisioningState ResourceGroup ResourceUid SupportPlan + ------------------------------------------------------------------------------ -------------------------- ---------------------- ---------------------------------- ------------ ----------------------------------------------------------------------- ------------------- ------------- --------------- ------------------ ----------------------------------------------------- ------------------- ----------------------- ------------------------------------ ------------------ + apache-air-apache-airflow-r-363a0a-rhf6saad.portal.hcp.$MY_LOCATION.azmk8s.io 1.29.9 False apache-air-apache-airflow-r-363a0a True apache-air-apache-airflow-r-363a0a-rhf6saad.hcp.$MY_LOCATION.azmk8s.io 1.29 $MY_LOCATION 100 $MY_CLUSTER_NAME MC_apache-airflow-rg_apache-airflow-aks_$MY_LOCATION Succeeded $MY_RESOURCE_GROUP_NAME b1b1b1b1-cccc-dddd-eeee-f2f2f2f2f2f2 KubernetesOfficial + ``` + +2. Get the OIDC issuer URL to use for the workload identity configuration using the [`az aks show`](/cli/azure/aks#az-aks-show) command. + + ```azurecli-interactive + export OIDC_URL=$(az aks show --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_CLUSTER_NAME --query oidcIssuerProfile.issuerUrl --output tsv) + ``` + +3. Assign the `AcrPull` role to the kubelet identity using the [`az role assignment create`](/cli/azure/role/assignment#az-role-assignment-create) command. + + ```azurecli-interactive + export KUBELET_IDENTITY=$(az aks show -g $MY_RESOURCE_GROUP_NAME --name $MY_CLUSTER_NAME --output tsv --query identityProfile.kubeletidentity.objectId) + az role assignment create \ + --assignee ${KUBELET_IDENTITY} \ + --role "AcrPull" \ + --scope ${MY_ACR_REGISTRY_ID} \ + --output table + ``` + + Example output: + + ```output + CreatedBy CreatedOn Name PrincipalId PrincipalName PrincipalType ResourceGroup RoleDefinitionId RoleDefinitionName Scope UpdatedBy UpdatedOn + ------------------------------------ -------------------------------- ------------------------------------ ------------------------------------ ------------------------------------ ---------------- ----------------------- ------------------------------------------------------------------------------------------------------------------------------------------ -------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------ -------------------------------- + ccccdddd-2222-eeee-3333-ffff4444aaaa 2024-11-07T00:43:26.905445+00:00 b1b1b1b1-cccc-dddd-eeee-f2f2f2f2f2f2 bbbbbbbb-cccc-dddd-2222-333333333333 cccccccc-dddd-eeee-3333-444444444444 ServicePrincipal $MY_RESOURCE_GROUP_NAME /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/providers/Microsoft.Authorization/roleDefinitions/7f951dda-4ed3-4680-a7ca-43fe172d538d AcrPull /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/$MY_RESOURCE_GROUP_NAME/providers/Microsoft.ContainerRegistry/registries/mydnsrandomnamebfbje ccccdddd-2222-eeee-3333-ffff4444aaaa 2024-11-07T00:43:26.905445+00:00 + ``` + +## Connect to the AKS cluster + +* Configure `kubectl` to connect to your AKS cluster using the [`az aks get-credentials`](/cli/azure/aks#az-aks-get-credentials) command. + + ```azurecli-interactive + az aks get-credentials --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_CLUSTER_NAME --overwrite-existing --output table + ``` + +## Upload Apache Airflow images to your container registry + +In this section, we download the Apache Airflow images from Docker Hub and upload them to Azure Container Registry. This step ensures that the images are available in your private registry and can be used in your AKS cluster. We don't recommend consuming the public image in a production environment. + +* Import the Airflow images from Docker Hub and upload them to your container registry using the [`az acr import`](/cli/azure/acr#az-acr-import) command. + + ```azurecli-interactive + az acr import --name $MY_ACR_REGISTRY --source docker.io/apache/airflow:airflow-pgbouncer-2024.01.19-1.21.0 --image airflow:airflow-pgbouncer-2024.01.19-1.21.0 + az acr import --name $MY_ACR_REGISTRY --source docker.io/apache/airflow:airflow-pgbouncer-exporter-2024.06.18-0.17.0 --image airflow:airflow-pgbouncer-exporter-2024.06.18-0.17.0 + az acr import --name $MY_ACR_REGISTRY --source docker.io/bitnami/postgresql:16.1.0-debian-11-r15 --image postgresql:16.1.0-debian-11-r15 + az acr import --name $MY_ACR_REGISTRY --source quay.io/prometheus/statsd-exporter:v0.26.1 --image statsd-exporter:v0.26.1 + az acr import --name $MY_ACR_REGISTRY --source docker.io/apache/airflow:2.9.3 --image airflow:2.9.3 + az acr import --name $MY_ACR_REGISTRY --source registry.k8s.io/git-sync/git-sync:v4.1.0 --image git-sync:v4.1.0 + ``` + +## Next step + +> [!div class="nextstepaction"] +> [Deploy Apache Airflow on Azure Kubernetes Service (AKS)](./airflow-deploy.md) + +## Contributors + +*Microsoft maintains this article. The following contributors originally wrote it:* + +* Don High | Principal Customer Engineer +* Satya Chandragiri | Senior Digital Cloud Solution Architect +* Erin Schaffer | Content Developer 2 \ No newline at end of file diff --git a/scenarios/azure-aks-docs/articles/aks/airflow-deploy.md b/scenarios/azure-aks-docs/articles/aks/airflow-deploy.md new file mode 100644 index 000000000..5fbbb7bd2 --- /dev/null +++ b/scenarios/azure-aks-docs/articles/aks/airflow-deploy.md @@ -0,0 +1,524 @@ +--- +title: Configure and deploy Apache Airflow on Azure Kubernetes Service (AKS) +description: In this article, you configure and deploy Apache Airflow on Azure Kubernetes Service (AKS) using Helm. +ms.topic: how-to +ms.custom: azure-kubernetes-service +ms.date: 12/19/2024 +author: schaffererin +ms.author: schaffererin +--- + +# Configure and deploy Airflow on Azure Kubernetes Service (AKS) + +In this article, you configure and deploy Apache Airflow on Azure Kubernetes Service (AKS) using Helm. + +## Configure workload identity + +1. Create a namespace for the Airflow cluster using the `kubectl create namespace` command. + + ```bash + kubectl create namespace ${AKS_AIRFLOW_NAMESPACE} --dry-run=client --output yaml | kubectl apply -f - + ``` + + Example output: + + ```output + namespace/airflow created + ``` + +2. Create a service account and configure workload identity using the `kubectl apply` command. + + ```bash + export TENANT_ID=$(az account show --query tenantId -o tsv) + cat < + ```output + serviceaccount/airflow created + ``` + +## Install the External Secrets Operator + +In this section, we use Helm to install the External Secrets Operator. The External Secrets Operator is a Kubernetes operator that manages the lifecycle of external secrets stored in external secret stores like Azure Key Vault. + +1. Add the External Secrets Helm repository and update the repository using the `helm repo add` and `helm repo update` commands. + + ```bash + helm repo add external-secrets https://charts.external-secrets.io + helm repo update + ``` + + Example output: + + ```output + Hang tight while we grab the latest from your chart repositories... + ...Successfully got an update from the "external-secrets" chart repository + ``` + +2. Install the External Secrets Operator using the `helm install` command. + + ```bash + helm install external-secrets \ + external-secrets/external-secrets \ + --namespace ${AKS_AIRFLOW_NAMESPACE} \ + --create-namespace \ + --set installCRDs=true \ + --wait + ``` + + Example output: + + ```output + NAME: external-secrets + LAST DEPLOYED: Thu Nov 7 11:16:07 2024 + NAMESPACE: airflow + STATUS: deployed + REVISION: 1 + TEST SUITE: None + NOTES: + external-secrets has been deployed successfully in namespace airflow! + + In order to begin using ExternalSecrets, you will need to set up a SecretStore + or ClusterSecretStore resource (for example, by creating a 'vault' SecretStore). + + More information on the different types of SecretStores and how to configure them + can be found in our Github: https://github.com/external-secrets/external-secrets + ``` + +### Create secrets + +1. Create a `SecretStore` resource to access the Airflow passwords stored in your key vault using the `kubectl apply` command. + + ```bash + kubectl apply -f - < + ```output + secretstore.external-secrets.io/azure-store created + ``` + +2. Create an `ExternalSecret` resource, which creates a Kubernetes `Secret` in the `airflow` namespace with the `Airflow` secrets stored in your key vault, using the `kubectl apply` command. + + ```bash + kubectl apply -f - < + ```output + externalsecret.external-secrets.io/airflow-aks-azure-logs-secrets created + ``` + +3. Create a federated credential using the `az identity federated-credential create` command. + + ```azurecli-interactive + az identity federated-credential create \ + --name external-secret-operator \ + --identity-name ${MY_IDENTITY_NAME} \ + --resource-group ${MY_RESOURCE_GROUP_NAME} \ + --issuer ${OIDC_URL} \ + --subject system:serviceaccount:${AKS_AIRFLOW_NAMESPACE}:${SERVICE_ACCOUNT_NAME} \ + --output table + ``` + + Example output: + + ```output + Issuer Name ResourceGroup Subject + ----------------------------------------------------------------------------------------------------------------------- ------------------------ ----------------------- ------------------------------------- + https://$MY_LOCATION.oic.prod-aks.azure.com/c2c2c2c2-dddd-eeee-ffff-a3a3a3a3a3a3/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/ external-secret-operator $MY_RESOURCE_GROUP_NAME system:serviceaccount:airflow:airflow + ``` + +4. Give permission to the user-assigned identity to access the secret using the [`az keyvault set-policy`](/cli/azure/keyvault#az-keyvault-set-policy) command. + + ```azurecli-interactive + az keyvault set-policy --name $MY_KEYVAULT_NAME --object-id $MY_IDENTITY_NAME_PRINCIPAL_ID --secret-permissions get --output table + ``` + + Example output: + + ```output + Location Name ResourceGroup + ------------- ---------------------- ----------------------- + $MY_LOCATION $MY_KEYVAULT_NAME $MY_RESOURCE_GROUP_NAME + ``` + +## Create a persistent volume for Apache Airflow logs + +* Create a persistent volume using the `kubectl apply` command. + + ```bash + kubectl apply -f - < airflow_values.yaml + + images: + airflow: + repository: $MY_ACR_REGISTRY.azurecr.io/airflow + tag: 2.9.3 + # Specifying digest takes precedence over tag. + digest: ~ + pullPolicy: IfNotPresent + # To avoid images with user code, you can turn this to 'true' and + # all the 'run-airflow-migrations' and 'wait-for-airflow-migrations' containers/jobs + # will use the images from 'defaultAirflowRepository:defaultAirflowTag' values + # to run and wait for DB migrations . + useDefaultImageForMigration: false + # timeout (in seconds) for airflow-migrations to complete + migrationsWaitTimeout: 60 + pod_template: + # Note that `images.pod_template.repository` and `images.pod_template.tag` parameters + # can be overridden in `config.kubernetes` section. So for these parameters to have effect + # `config.kubernetes.worker_container_repository` and `config.kubernetes.worker_container_tag` + # must be not set . + repository: $MY_ACR_REGISTRY.azurecr.io/airflow + tag: 2.9.3 + pullPolicy: IfNotPresent + flower: + repository: $MY_ACR_REGISTRY.azurecr.io/airflow + tag: 2.9.3 + pullPolicy: IfNotPresent + statsd: + repository: $MY_ACR_REGISTRY.azurecr.io/statsd-exporter + tag: v0.26.1 + pullPolicy: IfNotPresent + pgbouncer: + repository: $MY_ACR_REGISTRY.azurecr.io/airflow + tag: airflow-pgbouncer-2024.01.19-1.21.0 + pullPolicy: IfNotPresent + pgbouncerExporter: + repository: $MY_ACR_REGISTRY.azurecr.io/airflow + tag: airflow-pgbouncer-exporter-2024.06.18-0.17.0 + pullPolicy: IfNotPresent + gitSync: + repository: $MY_ACR_REGISTRY.azurecr.io/git-sync + tag: v4.1.0 + pullPolicy: IfNotPresent + + + # Airflow executor + executor: "KubernetesExecutor" + + # Environment variables for all airflow containers + env: + - name: ENVIRONMENT + value: dev + + extraEnv: | + - name: AIRFLOW__CORE__DEFAULT_TIMEZONE + value: 'America/New_York' + + # Configuration for postgresql subchart + # Not recommended for production! Instead, spin up your own Postgresql server and use the `data` attribute in this + # yaml file. + postgresql: + enabled: true + + # Enable pgbouncer. See https://airflow.apache.org/docs/helm-chart/stable/production-guide.html#pgbouncer + pgbouncer: + enabled: true + + dags: + gitSync: + enabled: true + repo: https://github.com/donhighmsft/airflowexamples.git + branch: main + rev: HEAD + depth: 1 + maxFailures: 0 + subPath: "dags" + # sshKeySecret: airflow-git-ssh-secret + # knownHosts: | + # github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + + logs: + persistence: + enabled: true + existingClaim: pvc-airflow-logs + storageClassName: azureblob-fuse-premium + + # We disable the log groomer sidecar because we use Azure Blob Storage for logs, with lifecyle policy set. + triggerer: + logGroomerSidecar: + enabled: false + + scheduler: + logGroomerSidecar: + enabled: false + + workers: + logGroomerSidecar: + enabled: false + + EOF + ``` + +2. Add the Apache Airflow Helm repository and update the repository using the `helm repo add` and `helm repo update` commands. + + ```bash + helm repo add apache-airflow https://airflow.apache.org + helm repo update + ``` + + Example output: + + ```output + "apache-airflow" has been added to your repositories + Hang tight while we grab the latest from your chart repositories... + ...Successfully got an update from the "apache-airflow" chart repository + ``` + +3. Search the Helm repository for the Apache Airflow chart using the `helm search repo` command. + + ```bash + helm search repo airflow + ``` + + Example output: + + ```output + NAME CHART VERSION APP VERSION DESCRIPTION + apache-airflow/airflow 1.15.0 2.9.3 The official Helm chart to deploy Apache Airflo... + ``` + +4. Install the Apache Airflow chart using the `helm install` command. + + ```bash + if ! helm list --namespace ${AKS_AIRFLOW_NAMESPACE} | grep -q external-secrets; then + helm install external-secrets \ + external-secrets/external-secrets \ + --namespace ${AKS_AIRFLOW_NAMESPACE} \ + --create-namespace \ + --set installCRDs=true \ + --wait + else + echo "External Secrets Operator is already installed." + fi + ``` + + Example output: + + ```output + NAME: airflow + LAST DEPLOYED: Fri Nov 8 11:59:43 2024 + NAMESPACE: airflow + STATUS: deployed + REVISION: 1 + TEST SUITE: None + NOTES: + Thank you for installing Apache Airflow 2.9.3! + + Your release is named airflow. + You can now access your dashboard(s) by executing the following command(s) and visiting the corresponding port at localhost in your browser: + + Airflow Webserver: kubectl port-forward svc/airflow-webserver 8080:8080 --namespace airflow + Default Webserver (Airflow UI) Login credentials: + username: admin + password: admin + Default Postgres connection credentials: + username: postgres + password: postgres + port: 5432 + + You can get Fernet Key value by running the following: + + echo Fernet Key: $(kubectl get secret --namespace airflow airflow-fernet-key -o jsonpath="{.data.fernet-key}" | base64 --decode) + + ########################################################### + # WARNING: You should set a static webserver secret key # + ########################################################### + + You are using a dynamically generated webserver secret key, which can lead to + unnecessary restarts of your Airflow components. + + Information on how to set a static webserver secret key can be found here: + https://airflow.apache.org/docs/helm-chart/stable/production-guide.html#webserver-secret-key + ``` + +5. Verify the installation using the `kubectl get pods` command. + + ```bash + kubectl get pods -n airflow + ``` + + Example output: + + ```output + NAME READY STATUS RESTARTS AGE + airflow-create-user-kklqf 1/1 Running 0 12s + airflow-pgbouncer-d7bf9f649-25fnt 2/2 Running 0 61s + airflow-postgresql-0 1/1 Running 0 61s + airflow-run-airflow-migrations-zns2b 0/1 Completed 0 60s + airflow-scheduler-5c45c6dbdd-7t6hv 1/2 Running 0 61s + airflow-statsd-6df8564664-6rbw8 1/1 Running 0 61s + airflow-triggerer-0 2/2 Running 0 61s + airflow-webserver-7df76f944c-vcd5s 0/1 Running 0 61s + external-secrets-748f44c8b8-w7qrk 1/1 Running 0 3h6m + external-secrets-cert-controller-57b9f4cb7c-vl4m8 1/1 Running 0 3h6m + external-secrets-webhook-5954b69786-69rlp 1/1 Running 0 3h6m + ``` + +## Access Airflow UI + +1. Securely access the Airflow UI through port-forwarding using the `kubectl port-forward` command. + + `kubectl port-forward svc/airflow-webserver 8080:8080 -n airflow` + +2. Open your browser and navigate to `localhost:8080` to access the Airflow UI. +3. Use the default webserver URL and login credentials provided during the Airflow Helm chart installation to log in. +4. Explore and manage your workflows securely through the Airflow UI. + +## Integrate Git with Airflow + +**Integrating Git with Apache Airflow** enables seamless version control and streamlined management of your workflow definitions, ensuring that all DAGs are both organized and easily auditable. + +1. **Set up a Git repository for DAGs**. Create a dedicated Git repository to house all your Airflow DAG definitions. This repository serves as the central source of truth for your workflows, allowing you to manage, track, and collaborate on DAGs effectively. +2. **Configure Airflow to sync DAGs from Git**. Update Airflow’s configuration to automatically pull DAGs from your Git repository by setting the Git repository URL and any required authentication credentials directly in Airflow’s configuration files or through Helm chart values. This setup enables automated synchronization of DAGs, ensuring that Airflow is always up to date with the latest version of your workflows. + +This integration enhances the development and deployment workflow by introducing full version control, enabling rollbacks, and supporting team collaboration in a production-grade setup. + +## Make your Airflow on Kubernetes production-grade + +The following best practices can help you make your **Apache Airflow on Kubernetes** deployment production-grade: + +* Ensure you have a robust setup focused on scalability, security, and reliability. +* Use dedicated, autoscaling nodes, and select a resilient executor like **KubernetesExecutor**, **CeleryExecutor**, or **CeleryKubernetesExecutor**. +* Use a managed, high-availability database back end like MySQL or [PostgreSQL](./deploy-postgresql-ha.md). +* Establish comprehensive monitoring and centralized logging to maintain performance insights. +* Secure your environment with network policies, SSL, and Role-Based Access Control (RBAC), and configure Airflow components (Scheduler, Web Server, Workers) for high availability. +* Implement CI/CD pipelines for smooth DAG deployment, and set up regular backups for disaster recovery. + +## Next steps + +To learn more about deploy open-source software on Azure Kubernetes Service (AKS), see the following articles: + +* [Deploy a MongoDB cluster on Azure Kubernetes Service (AKS)](./mongodb-overview.md) +* [Deploy a highly available PostgreSQL database on Azure Kubernetes Service (AKS)](./postgresql-ha-overview.md) +* [Deploy a Valkey cluster on Azure Kubernetes Service (AKS)](./valkey-overview.md) + +## Contributors + +*Microsoft maintains this article. The following contributors originally wrote it:* + +* Don High | Principal Customer Engineer +* Satya Chandragiri | Senior Digital Cloud Solution Architect +* Erin Schaffer | Content Developer 2 \ No newline at end of file diff --git a/scenarios/azure-aks-docs/articles/aks/create-postgresql-ha.md b/scenarios/azure-aks-docs/articles/aks/create-postgresql-ha.md new file mode 100644 index 000000000..13c346d4a --- /dev/null +++ b/scenarios/azure-aks-docs/articles/aks/create-postgresql-ha.md @@ -0,0 +1,545 @@ +--- +title: 'Create infrastructure for deploying a highly available PostgreSQL database on AKS' +description: Create the infrastructure needed to deploy a highly available PostgreSQL database on AKS using the CloudNativePG operator. +ms.topic: how-to +ms.date: 06/07/2024 +author: kenkilty +ms.author: kkilty +ms.custom: innovation-engine, aks-related-content +--- + +# Create infrastructure for deploying a highly available PostgreSQL database on AKS + +In this article, you create the infrastructure needed to deploy a highly available PostgreSQL database on AKS using the [CloudNativePG (CNPG)](https://cloudnative-pg.io/) operator. + +[!INCLUDE [open source disclaimer](./includes/open-source-disclaimer.md)] + +## Before you begin + +* Review the deployment overview and make sure you meet all the prerequisites in [How to deploy a highly available PostgreSQL database on AKS with Azure CLI][postgresql-ha-deployment-overview]. +* [Set environment variables](#set-environment-variables) for use throughout this guide. +* [Install the required extensions](#install-required-extensions). + +## Install required extensions + +The `aks-preview`, `k8s-extension` and `amg` extensions provide more functionality for managing Kubernetes clusters and querying Azure resources. Install these extensions using the following [`az extension add`][az-extension-add] commands: + +```bash +az extension add --upgrade --name aks-preview --yes --allow-preview true +az extension add --upgrade --name k8s-extension --yes --allow-preview false +az extension add --upgrade --name amg --yes --allow-preview false +``` + +As a prerequisite for utilizing kubectl, it is essential to first install [Krew][install-krew], followed by the installation of the [CNPG plugin][cnpg-plugin]. This will enable the management of the PostgreSQL operator using the subsequent commands. + +```bash +( + set -x; cd "$(mktemp -d)" && + OS="$(uname | tr '[:upper:]' '[:lower:]')" && + ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" && + KREW="krew-${OS}_${ARCH}" && + curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" && + tar zxvf "${KREW}.tar.gz" && + ./"${KREW}" install krew +) + +export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH" + +kubectl krew install cnpg +``` + +## Create a resource group + +Create a resource group to hold the resources you create in this guide using the [`az group create`][az-group-create] command. + +```bash +export TAGS="owner=user" +export LOCAL_NAME="cnpg" +export RESOURCE_GROUP_NAME="rg-${LOCAL_NAME}-${SUFFIX}" +export PRIMARY_CLUSTER_REGION="westus3" +az group create \ + --name $RESOURCE_GROUP_NAME \ + --location $PRIMARY_CLUSTER_REGION \ + --tags $TAGS \ + --query 'properties.provisioningState' \ + --output tsv +``` + +## Create a user-assigned managed identity + +In this section, you create a user-assigned managed identity (UAMI) to allow the CNPG PostgreSQL to use an AKS workload identity to access Azure Blob Storage. This configuration allows the PostgreSQL cluster on AKS to connect to Azure Blob Storage without a secret. + +1. Create a user-assigned managed identity using the [`az identity create`][az-identity-create] command. + + ```bash + export SUFFIX=$(cat /dev/urandom | LC_ALL=C tr -dc 'a-z0-9' | fold -w 8 | head -n 1) + export AKS_UAMI_CLUSTER_IDENTITY_NAME="mi-aks-${LOCAL_NAME}-${SUFFIX}" + AKS_UAMI_WI_IDENTITY=$(az identity create \ + --name $AKS_UAMI_CLUSTER_IDENTITY_NAME \ + --resource-group $RESOURCE_GROUP_NAME \ + --location $PRIMARY_CLUSTER_REGION \ + --output json) + ``` + +1. Enable AKS workload identity and generate a service account to use later in this guide using the following commands: + + ```bash + export AKS_UAMI_WORKLOAD_OBJECTID=$( \ + echo "${AKS_UAMI_WI_IDENTITY}" | jq -r '.principalId') + export AKS_UAMI_WORKLOAD_RESOURCEID=$( \ + echo "${AKS_UAMI_WI_IDENTITY}" | jq -r '.id') + export AKS_UAMI_WORKLOAD_CLIENTID=$( \ + echo "${AKS_UAMI_WI_IDENTITY}" | jq -r '.clientId') + + echo "ObjectId: $AKS_UAMI_WORKLOAD_OBJECTID" + echo "ResourceId: $AKS_UAMI_WORKLOAD_RESOURCEID" + echo "ClientId: $AKS_UAMI_WORKLOAD_CLIENTID" + ``` + +The object ID is a unique identifier for the client ID (also known as the application ID) that uniquely identifies a security principal of type *Application* within the Microsoft Entra ID tenant. The resource ID is a unique identifier to manage and locate a resource in Azure. These values are required to enabled AKS workload identity. + +The CNPG operator automatically generates a service account called *postgres* that you use later in the guide to create a federated credential that enables OAuth access from PostgreSQL to Azure Storage. + +## Create a storage account in the primary region + +1. Create an object storage account to store PostgreSQL backups in the primary region using the [`az storage account create`][az-storage-account-create] command. + + ```bash + export PG_PRIMARY_STORAGE_ACCOUNT_NAME="hacnpgpsa${SUFFIX}" + + az storage account create \ + --name $PG_PRIMARY_STORAGE_ACCOUNT_NAME \ + --resource-group $RESOURCE_GROUP_NAME \ + --location $PRIMARY_CLUSTER_REGION \ + --sku Standard_ZRS \ + --kind StorageV2 \ + --query 'provisioningState' \ + --output tsv + ``` + +1. Create the storage container to store the Write Ahead Logs (WAL) and regular PostgreSQL on-demand and scheduled backups using the [`az storage container create`][az-storage-container-create] command. + + ```bash + export PG_STORAGE_BACKUP_CONTAINER_NAME="backups" + + az storage container create \ + --name $PG_STORAGE_BACKUP_CONTAINER_NAME \ + --account-name $PG_PRIMARY_STORAGE_ACCOUNT_NAME \ + --auth-mode login + ``` + + Example output: + + ```output + { + "created": true + } + ``` + + > [!NOTE] + > If you encounter the error message: `The request may be blocked by network rules of storage account. Please check network rule set using 'az storage account show -n accountname --query networkRuleSet'. If you want to change the default action to apply when no rule matches, please use 'az storage account update'`. Please verify user permissions for Azure Blob Storage and, if **necessary**, elevate your role to `Storage Blob Data Owner` using the commands provided below and after retry the [`az storage container create`][az-storage-container-create] command. + + ```bash + export USER_ID=$(az ad signed-in-user show --query id --output tsv) + + export STORAGE_ACCOUNT_PRIMARY_RESOURCE_ID=$(az storage account show \ + --name $PG_PRIMARY_STORAGE_ACCOUNT_NAME \ + --resource-group $RESOURCE_GROUP_NAME \ + --query "id" \ + --output tsv) + + az role assignment list --scope $STORAGE_ACCOUNT_PRIMARY_RESOURCE_ID --output table + + az role assignment create \ + --role "Storage Blob Data Contributor" \ + --assignee-object-id $AKS_UAMI_WORKLOAD_OBJECTID \ + --assignee-principal-type ServicePrincipal \ + --scope $STORAGE_ACCOUNT_PRIMARY_RESOURCE_ID \ + --query "id" \ + --output tsv + ``` + +## Assign RBAC to storage accounts + +To enable backups, the PostgreSQL cluster needs to read and write to an object store. The PostgreSQL cluster running on AKS uses a workload identity to access the storage account via the CNPG operator configuration parameter [`inheritFromAzureAD`][inherit-from-azuread]. + +1. Get the primary resource ID for the storage account using the [`az storage account show`][az-storage-account-show] command. + + ```bash + export STORAGE_ACCOUNT_PRIMARY_RESOURCE_ID=$(az storage account show \ + --name $PG_PRIMARY_STORAGE_ACCOUNT_NAME \ + --resource-group $RESOURCE_GROUP_NAME \ + --query "id" \ + --output tsv) + + echo $STORAGE_ACCOUNT_PRIMARY_RESOURCE_ID + ```` + +1. Assign the "Storage Blob Data Contributor" Azure built-in role to the object ID with the storage account resource ID scope for the UAMI associated with the managed identity for each AKS cluster using the [`az role assignment create`][az-role-assignment-create] command. + + ```bash + az role assignment create \ + --role "Storage Blob Data Contributor" \ + --assignee-object-id $AKS_UAMI_WORKLOAD_OBJECTID \ + --assignee-principal-type ServicePrincipal \ + --scope $STORAGE_ACCOUNT_PRIMARY_RESOURCE_ID \ + --query "id" \ + --output tsv + ``` + +## Set up monitoring infrastructure + +In this section, you deploy an instance of Azure Managed Grafana, an Azure Monitor workspace, and an Azure Monitor Log Analytics workspace to enable monitoring of the PostgreSQL cluster. You also store references to the created monitoring infrastructure to use as input during the AKS cluster creation process later in the guide. This section might take some time to complete. + +> [!NOTE] +> Azure Managed Grafana instances and AKS clusters are billed independently. For more pricing information, see [Azure Managed Grafana pricing][azure-managed-grafana-pricing]. + +1. Create an Azure Managed Grafana instance using the [`az grafana create`][az-grafana-create] command. + + ```bash + export GRAFANA_PRIMARY="grafana-${LOCAL_NAME}-${SUFFIX}" + + export GRAFANA_RESOURCE_ID=$(az grafana create \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $GRAFANA_PRIMARY \ + --location $PRIMARY_CLUSTER_REGION \ + --zone-redundancy Enabled \ + --tags $TAGS \ + --query "id" \ + --output tsv) + + echo $GRAFANA_RESOURCE_ID + ``` + +1. Create an Azure Monitor workspace using the [`az monitor account create`][az-monitor-account-create] command. + + ```bash + export AMW_PRIMARY="amw-${LOCAL_NAME}-${SUFFIX}" + + export AMW_RESOURCE_ID=$(az monitor account create \ + --name $AMW_PRIMARY \ + --resource-group $RESOURCE_GROUP_NAME \ + --location $PRIMARY_CLUSTER_REGION \ + --tags $TAGS \ + --query "id" \ + --output tsv) + + echo $AMW_RESOURCE_ID + ``` + +1. Create an Azure Monitor Log Analytics workspace using the [`az monitor log-analytics workspace create`][az-monitor-log-analytics-workspace-create] command. + + ```bash + export ALA_PRIMARY="ala-${LOCAL_NAME}-${SUFFIX}" + + export ALA_RESOURCE_ID=$(az monitor log-analytics workspace create \ + --resource-group $RESOURCE_GROUP_NAME \ + --workspace-name $ALA_PRIMARY \ + --location $PRIMARY_CLUSTER_REGION \ + --query "id" \ + --output tsv) + + echo $ALA_RESOURCE_ID + ``` + +## Create the AKS cluster to host the PostgreSQL cluster + +In this section, you create a multizone AKS cluster with a system node pool. The AKS cluster hosts the PostgreSQL cluster primary replica and two standby replicas, each aligned to a different availability zone to enable zonal redundancy. + +You also add a user node pool to the AKS cluster to host the PostgreSQL cluster. Using a separate node pool allows for control over the Azure VM SKUs used for PostgreSQL and enables the AKS system pool to optimize performance and costs. You apply a label to the user node pool that you can reference for node selection when deploying the CNPG operator later in this guide. This section might take some time to complete. + +1. Create an AKS cluster using the [`az aks create`][az-aks-create] command. + + ```bash + export SYSTEM_NODE_POOL_VMSKU="standard_d2s_v3" + export USER_NODE_POOL_NAME="postgres" + export USER_NODE_POOL_VMSKU="standard_d4s_v3" + export AKS_PRIMARY_CLUSTER_NAME="aks-primary-${LOCAL_NAME}-${SUFFIX}" + export AKS_PRIMARY_MANAGED_RG_NAME="rg-${LOCAL_NAME}-primary-aksmanaged-${SUFFIX}" + export AKS_CLUSTER_VERSION="1.29" + export MY_PUBLIC_CLIENT_IP=$(dig +short myip.opendns.com @resolver3.opendns.com) + + az aks create \ + --name $AKS_PRIMARY_CLUSTER_NAME \ + --tags $TAGS \ + --resource-group $RESOURCE_GROUP_NAME \ + --location $PRIMARY_CLUSTER_REGION \ + --generate-ssh-keys \ + --node-resource-group $AKS_PRIMARY_MANAGED_RG_NAME \ + --enable-managed-identity \ + --assign-identity $AKS_UAMI_WORKLOAD_RESOURCEID \ + --network-plugin azure \ + --network-plugin-mode overlay \ + --network-dataplane cilium \ + --nodepool-name systempool \ + --enable-oidc-issuer \ + --enable-workload-identity \ + --enable-cluster-autoscaler \ + --min-count 2 \ + --max-count 3 \ + --node-vm-size $SYSTEM_NODE_POOL_VMSKU \ + --enable-azure-monitor-metrics \ + --azure-monitor-workspace-resource-id $AMW_RESOURCE_ID \ + --grafana-resource-id $GRAFANA_RESOURCE_ID \ + --api-server-authorized-ip-ranges $MY_PUBLIC_CLIENT_IP \ + --tier standard \ + --kubernetes-version $AKS_CLUSTER_VERSION \ + --zones 1 2 3 \ + --output table + ``` + +2. Add a user node pool to the AKS cluster using the [`az aks nodepool add`][az-aks-node-pool-add] command. + + ```bash + az aks nodepool add \ + --resource-group $RESOURCE_GROUP_NAME \ + --cluster-name $AKS_PRIMARY_CLUSTER_NAME \ + --name $USER_NODE_POOL_NAME \ + --enable-cluster-autoscaler \ + --min-count 3 \ + --max-count 6 \ + --node-vm-size $USER_NODE_POOL_VMSKU \ + --zones 1 2 3 \ + --labels workload=postgres \ + --output table + ``` + +> [!NOTE] +> If you receive the error message `"(OperationNotAllowed) Operation is not allowed: Another operation (Updating) is in progress, please wait for it to finish before starting a new operation."` when adding the AKS node pool, please wait a few minutes for the AKS cluster operations to complete and then run the `az aks nodepool add` command. + +## Connect to the AKS cluster and create namespaces + +In this section, you get the AKS cluster credentials, which serve as the keys that allow you to authenticate and interact with the cluster. Once connected, you create two namespaces: one for the CNPG controller manager services and one for the PostgreSQL cluster and its related services. + +1. Get the AKS cluster credentials using the [`az aks get-credentials`][az-aks-get-credentials] command. + + ```bash + az aks get-credentials \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $AKS_PRIMARY_CLUSTER_NAME \ + --output none + ``` + +2. Create the namespace for the CNPG controller manager services, the PostgreSQL cluster, and its related services by using the [`kubectl create namespace`][kubectl-create-namespace] command. + + ```bash + export PG_NAMESPACE="cnpg-database" + export PG_SYSTEM_NAMESPACE="cnpg-system" + + kubectl create namespace $PG_NAMESPACE --context $AKS_PRIMARY_CLUSTER_NAME + kubectl create namespace $PG_SYSTEM_NAMESPACE --context $AKS_PRIMARY_CLUSTER_NAME + ``` + +## Update the monitoring infrastructure + +The Azure Monitor workspace for Managed Prometheus and Azure Managed Grafana are automatically linked to the AKS cluster for metrics and visualization during the cluster creation process. In this section, you enable log collection with AKS Container insights and validate that Managed Prometheus is scraping metrics and Container insights is ingesting logs. + +1. Enable Container insights monitoring on the AKS cluster using the [`az aks enable-addons`][az-aks-enable-addons] command. + + ```bash + az aks enable-addons \ + --addon monitoring \ + --name $AKS_PRIMARY_CLUSTER_NAME \ + --resource-group $RESOURCE_GROUP_NAME \ + --workspace-resource-id $ALA_RESOURCE_ID \ + --output table + ``` + +2. Validate that Managed Prometheus is scraping metrics and Container insights is ingesting logs from the AKS cluster by inspecting the DaemonSet using the [`kubectl get`][kubectl-get] command and the [`az aks show`][az-aks-show] command. + + ```bash + kubectl get ds ama-metrics-node \ + --context $AKS_PRIMARY_CLUSTER_NAME \ + --namespace=kube-system + + kubectl get ds ama-logs \ + --context $AKS_PRIMARY_CLUSTER_NAME \ + --namespace=kube-system + + az aks show \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $AKS_PRIMARY_CLUSTER_NAME \ + --query addonProfiles + ``` + + Your output should resemble the following example output, with *six* nodes total (three for the system node pool and three for the PostgreSQL node pool) and the Container insights showing `"enabled": true`: + + ```output + NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR + ama-metrics-node 6 6 6 6 6 + + NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR + ama-logs 6 6 6 6 6 + + { + "omsagent": { + "config": { + "logAnalyticsWorkspaceResourceID": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-cnpg-9vbin3p8/providers/Microsoft.OperationalInsights/workspaces/ala-cnpg-9vbin3p8", + "useAADAuth": "true" + }, + "enabled": true, + "identity": null + } + } + ``` + +## Create a public static IP for PostgreSQL cluster ingress + +To validate deployment of the PostgreSQL cluster and use client PostgreSQL tooling, such as *psql* and *PgAdmin*, you need to expose the primary and read-only replicas to ingress. In this section, you create an Azure public IP resource that you later supply to an Azure load balancer to expose PostgreSQL endpoints for query. + +1. Get the name of the AKS cluster node resource group using the [`az aks show`][az-aks-show] command. + + ```bash + export AKS_PRIMARY_CLUSTER_NODERG_NAME=$(az aks show \ + --name $AKS_PRIMARY_CLUSTER_NAME \ + --resource-group $RESOURCE_GROUP_NAME \ + --query nodeResourceGroup \ + --output tsv) + + echo $AKS_PRIMARY_CLUSTER_NODERG_NAME + ``` + +2. Create the public IP address using the [`az network public-ip create`][az-network-public-ip-create] command. + + ```bash + export AKS_PRIMARY_CLUSTER_PUBLICIP_NAME="$AKS_PRIMARY_CLUSTER_NAME-pip" + + az network public-ip create \ + --resource-group $AKS_PRIMARY_CLUSTER_NODERG_NAME \ + --name $AKS_PRIMARY_CLUSTER_PUBLICIP_NAME \ + --location $PRIMARY_CLUSTER_REGION \ + --sku Standard \ + --zone 1 2 3 \ + --allocation-method static \ + --output table + ``` + +3. Get the newly created public IP address using the [`az network public-ip show`][az-network-public-ip-show] command. + + ```bash + export AKS_PRIMARY_CLUSTER_PUBLICIP_ADDRESS=$(az network public-ip show \ + --resource-group $AKS_PRIMARY_CLUSTER_NODERG_NAME \ + --name $AKS_PRIMARY_CLUSTER_PUBLICIP_NAME \ + --query ipAddress \ + --output tsv) + + echo $AKS_PRIMARY_CLUSTER_PUBLICIP_ADDRESS + ``` + +4. Get the resource ID of the node resource group using the [`az group show`][az-group-show] command. + + ```bash + export AKS_PRIMARY_CLUSTER_NODERG_NAME_SCOPE=$(az group show --name \ + $AKS_PRIMARY_CLUSTER_NODERG_NAME \ + --query id \ + --output tsv) + echo $AKS_PRIMARY_CLUSTER_NODERG_NAME_SCOPE + ``` + +5. Assign the "Network Contributor" role to the UAMI object ID using the node resource group scope using the [`az role assignment create`][az-role-assignment-create] command. + + ```bash + az role assignment create \ + --assignee-object-id ${AKS_UAMI_WORKLOAD_OBJECTID} \ + --assignee-principal-type ServicePrincipal \ + --role "Network Contributor" \ + --scope ${AKS_PRIMARY_CLUSTER_NODERG_NAME_SCOPE} + ``` + +## Install the CNPG operator in the AKS cluster + +In this section, you install the CNPG operator in the AKS cluster using Helm or a YAML manifest. + +### [Helm](#tab/helm) + +1. Add the CNPG Helm repo using the [`helm repo add`][helm-repo-add] command. + + ```bash + helm repo add cnpg https://cloudnative-pg.github.io/charts + ``` + +2. Upgrade the CNPG Helm repo and install it on the AKS cluster using the [`helm upgrade`][helm-upgrade] command with the `--install` flag. + + ```bash + helm upgrade --install cnpg \ + --namespace $PG_SYSTEM_NAMESPACE \ + --create-namespace \ + --kube-context=$AKS_PRIMARY_CLUSTER_NAME \ + cnpg/cloudnative-pg + ``` + +3. Verify the operator installation on the AKS cluster using the [`kubectl get`][kubectl-get] command. + + ```bash + kubectl get deployment \ + --context $AKS_PRIMARY_CLUSTER_NAME \ + --namespace $PG_SYSTEM_NAMESPACE cnpg-cloudnative-pg + ``` + +### [YAML](#tab/yaml) + +1. Install the CNPG operator on the AKS cluster using the [`kubectl apply`][kubectl-apply] command. + + ```bash + kubectl apply --context $AKS_PRIMARY_CLUSTER_NAME \ + --namespace $PG_SYSTEM_NAMESPACE \ + --server-side -f \ + https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.23/releases/cnpg-1.23.1.yaml + ``` + +2. Verify the operator installation on the AKS cluster using the [`kubectl get`][kubectl-get] command. + + ```bash + kubectl get deployment \ + --namespace $PG_SYSTEM_NAMESPACE cnpg-controller-manager \ + --context $AKS_PRIMARY_CLUSTER_NAME + ``` + +--- + +## Next steps + +> [!div class="nextstepaction"] +> [Deploy a highly available PostgreSQL database on the AKS cluster][deploy-postgresql] + +## Contributors + +*This article is maintained by Microsoft. It was originally written by the following contributors*: + +* Ken Kilty | Principal TPM +* Russell de Pina | Principal TPM +* Adrian Joian | Senior Customer Engineer +* Jenny Hayes | Senior Content Developer +* Carol Smith | Senior Content Developer +* Erin Schaffer | Content Developer 2 + + +[az-identity-create]: /cli/azure/identity#az-identity-create +[az-grafana-create]: /cli/azure/grafana#az-grafana-create +[postgresql-ha-deployment-overview]: ./postgresql-ha-overview.md +[az-extension-add]: /cli/azure/extension#az_extension_add +[az-group-create]: /cli/azure/group#az_group_create +[az-storage-account-create]: /cli/azure/storage/account#az_storage_account_create +[az-storage-container-create]: /cli/azure/storage/container#az_storage_container_create +[inherit-from-azuread]: https://cloudnative-pg.io/documentation/1.23/appendixes/object_stores/#azure-blob-storage +[az-storage-account-show]: /cli/azure/storage/account#az_storage_account_show +[az-role-assignment-create]: /cli/azure/role/assignment#az_role_assignment_create +[az-monitor-account-create]: /cli/azure/monitor/account#az_monitor_account_create +[az-monitor-log-analytics-workspace-create]: /cli/azure/monitor/log-analytics/workspace#az_monitor_log_analytics_workspace_create +[azure-managed-grafana-pricing]: https://azure.microsoft.com/pricing/details/managed-grafana/ +[az-aks-create]: /cli/azure/aks#az_aks_create +[az-aks-node-pool-add]: /cli/azure/aks/nodepool#az_aks_nodepool_add +[az-aks-get-credentials]: /cli/azure/aks#az_aks_get_credentials +[kubectl-create-namespace]: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_create/kubectl_create_namespace/ +[az-aks-enable-addons]: /cli/azure/aks#az_aks_enable_addons +[kubectl-get]: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_get/ +[az-aks-show]: /cli/azure/aks#az_aks_show +[az-network-public-ip-create]: /cli/azure/network/public-ip#az_network_public_ip_create +[az-network-public-ip-show]: /cli/azure/network/public-ip#az_network_public_ip_show +[az-group-show]: /cli/azure/group#az_group_show +[helm-repo-add]: https://helm.sh/docs/helm/helm_repo_add/ +[helm-upgrade]: https://helm.sh/docs/helm/helm_upgrade/ +[kubectl-apply]: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_apply/ +[deploy-postgresql]: ./deploy-postgresql-ha.md +[install-krew]: https://krew.sigs.k8s.io/ +[cnpg-plugin]: https://cloudnative-pg.io/documentation/current/kubectl-plugin/#using-krew \ No newline at end of file diff --git a/scenarios/azure-aks-docs/articles/aks/deploy-postgresql-ha.md b/scenarios/azure-aks-docs/articles/aks/deploy-postgresql-ha.md new file mode 100644 index 000000000..9f2eb91bd --- /dev/null +++ b/scenarios/azure-aks-docs/articles/aks/deploy-postgresql-ha.md @@ -0,0 +1,1000 @@ +--- +title: 'Deploy a highly available PostgreSQL database on AKS with Azure CLI' +description: In this article, you deploy a highly available PostgreSQL database on AKS using the CloudNativePG operator. +ms.topic: how-to +ms.date: 06/07/2024 +author: kenkilty +ms.author: kkilty +ms.custom: innovation-engine, aks-related-content +--- + +# Deploy a highly available PostgreSQL database on AKS + +In this article, you deploy a highly available PostgreSQL database on AKS. + +* If you haven't already created the required infrastructure for this deployment, follow the steps in [Create infrastructure for deploying a highly available PostgreSQL database on AKS][create-infrastructure] to get set up, and then you can return to this article. + +[!INCLUDE [open source disclaimer](./includes/open-source-disclaimer.md)] + +## Create secret for bootstrap app user + +1. Generate a secret to validate the PostgreSQL deployment by interactive login for a bootstrap app user using the [`kubectl create secret`][kubectl-create-secret] command. + + ```bash + export PG_DATABASE_APPUSER_SECRET=$(echo -n | openssl rand -base64 16) + + kubectl create secret generic db-user-pass \ + --from-literal=username=app \ + --from-literal=password="${PG_DATABASE_APPUSER_SECRET}" \ + --namespace $PG_NAMESPACE \ + --context $AKS_PRIMARY_CLUSTER_NAME + ``` + +1. Validate that the secret was successfully created using the [`kubectl get`][kubectl-get] command. + + ```bash + kubectl get secret db-user-pass --namespace $PG_NAMESPACE --context $AKS_PRIMARY_CLUSTER_NAME + ``` + +## Set environment variables for the PostgreSQL cluster + +* Deploy a ConfigMap to set environment variables for the PostgreSQL cluster using the following [`kubectl apply`][kubectl-apply] command: + + ```bash + export ENABLE_AZURE_PVC_UPDATES="true" + cat < 5432/TCP 3h57m + pg-primary-cnpg-sryti1qf-ro ClusterIP 10.0.237.19 5432/TCP 3h57m + pg-primary-cnpg-sryti1qf-rw ClusterIP 10.0.244.125 5432/TCP 3h57m + ``` + + > [!NOTE] + > There are three services: `namespace/cluster-name-ro` mapped to port 5433, `namespace/cluster-name-rw`, and `namespace/cluster-name-r` mapped to port 5433. It’s important to avoid using the same port as the read/write node of the PostgreSQL database cluster. If you want applications to access only the read-only replica of the PostgreSQL database cluster, direct them to port 5433. The final service is typically used for data backups but can also function as a read-only node. + +1. Get the service details using the [`kubectl get`][kubectl-get] command. + + ```bash + export PG_PRIMARY_CLUSTER_RW_SERVICE=$(kubectl get services \ + --namespace $PG_NAMESPACE \ + --context $AKS_PRIMARY_CLUSTER_NAME \ + -l "cnpg.io/cluster" \ + --output json | jq -r '.items[] | select(.metadata.name | endswith("-rw")) | .metadata.name') + + echo $PG_PRIMARY_CLUSTER_RW_SERVICE + + export PG_PRIMARY_CLUSTER_RO_SERVICE=$(kubectl get services \ + --namespace $PG_NAMESPACE \ + --context $AKS_PRIMARY_CLUSTER_NAME \ + -l "cnpg.io/cluster" \ + --output json | jq -r '.items[] | select(.metadata.name | endswith("-ro")) | .metadata.name') + + echo $PG_PRIMARY_CLUSTER_RO_SERVICE + ``` + +1. Configure the load balancer service with the following YAML files using the [`kubectl apply`][kubectl-apply] command. + + ```bash + cat < [!NOTE] +> You need the value of the app user password for PostgreSQL basic auth that was generated earlier and stored in the `$PG_DATABASE_APPUSER_SECRET` environment variable. + +* Validate the public PostgreSQL endpoints using the following `psql` commands: + + ```bash + echo "Public endpoint for PostgreSQL cluster: $AKS_PRIMARY_CLUSTER_ALB_DNSNAME" + + # Query the primary, pg_is_in_recovery = false + + psql -h $AKS_PRIMARY_CLUSTER_ALB_DNSNAME \ + -p 5432 -U app -d appdb -W -c "SELECT pg_is_in_recovery();" + ``` + + Example output + + ```output + pg_is_in_recovery + ------------------- + f + (1 row) + ``` + + ```bash + echo "Query a replica, pg_is_in_recovery = true" + + psql -h $AKS_PRIMARY_CLUSTER_ALB_DNSNAME \ + -p 5433 -U app -d appdb -W -c "SELECT pg_is_in_recovery();" + ``` + + Example output + + ```output + # Example output + + pg_is_in_recovery + ------------------- + t + (1 row) + ``` + + When successfully connected to the primary read-write endpoint, the PostgreSQL function returns `f` for *false*, indicating that the current connection is writable. + + When connected to a replica, the function returns `t` for *true*, indicating the database is in recovery and read-only. + +## Simulate an unplanned failover + +In this section, you trigger a sudden failure by deleting the pod running the primary, which simulates a sudden crash or loss of network connectivity to the node hosting the PostgreSQL primary. + +1. Check the status of the running pod instances using the following command: + + ```bash + kubectl cnpg status $PG_PRIMARY_CLUSTER_NAME --namespace $PG_NAMESPACE + ``` + + Example output + + ```output + Name Current LSN Rep role Status Node + --------------------------- ----------- -------- ------- ----------- + pg-primary-cnpg-sryti1qf-1 0/9000060 Primary OK aks-postgres-32388626-vmss000000 + pg-primary-cnpg-sryti1qf-2 0/9000060 Standby (sync) OK aks-postgres-32388626-vmss000001 + pg-primary-cnpg-sryti1qf-3 0/9000060 Standby (sync) OK aks-postgres-32388626-vmss000002 + ``` + +1. Delete the primary pod using the [`kubectl delete`][kubectl-delete] command. + + ```bash + PRIMARY_POD=$(kubectl get pod \ + --namespace $PG_NAMESPACE \ + --no-headers \ + -o custom-columns=":metadata.name" \ + -l role=primary) + + kubectl delete pod $PRIMARY_POD --grace-period=1 --namespace $PG_NAMESPACE + ``` + +1. Validate that the `pg-primary-cnpg-sryti1qf-2` pod instance is now the primary using the following command: + + ```bash + kubectl cnpg status $PG_PRIMARY_CLUSTER_NAME --namespace $PG_NAMESPACE + ``` + + Example output + + ```output + pg-primary-cnpg-sryti1qf-2 0/9000060 Primary OK aks-postgres-32388626-vmss000001 + pg-primary-cnpg-sryti1qf-1 0/9000060 Standby (sync) OK aks-postgres-32388626-vmss000000 + pg-primary-cnpg-sryti1qf-3 0/9000060 Standby (sync) OK aks-postgres-32388626-vmss000002 + ``` + +1. Reset the `pg-primary-cnpg-sryti1qf-1` pod instance as the primary using the following command: + + ```bash + kubectl cnpg promote $PG_PRIMARY_CLUSTER_NAME 1 --namespace $PG_NAMESPACE + ``` + +1. Validate that the pod instances have returned to their original state before the unplanned failover test using the following command: + + ```bash + kubectl cnpg status $PG_PRIMARY_CLUSTER_NAME --namespace $PG_NAMESPACE + ``` + + Example output + + ```output + Name Current LSN Rep role Status Node + --------------------------- ----------- -------- ------- ----------- + pg-primary-cnpg-sryti1qf-1 0/9000060 Primary OK aks-postgres-32388626-vmss000000 + pg-primary-cnpg-sryti1qf-2 0/9000060 Standby (sync) OK aks-postgres-32388626-vmss000001 + pg-primary-cnpg-sryti1qf-3 0/9000060 Standby (sync) OK aks-postgres-32388626-vmss000002 + ``` + +## Clean up resources + +* Once you're finished reviewing your deployment, delete all the resources you created in this guide using the [`az group delete`][az-group-delete] command. + + ```bash + az group delete --resource-group $RESOURCE_GROUP_NAME --no-wait --yes + ``` + +## Next steps + +In this how-to guide, you learned how to: + +* Use Azure CLI to create a multi-zone AKS cluster. +* Deploy a highly available PostgreSQL cluster and database using the CNPG operator. +* Set up monitoring for PostgreSQL using Prometheus and Grafana. +* Deploy a sample dataset to the PostgreSQL database. +* Perform PostgreSQL and AKS cluster upgrades. +* Simulate a cluster interruption and PostgreSQL replica failover. +* Perform a backup and restore of the PostgreSQL database. + +To learn more about how you can leverage AKS for your workloads, see [What is Azure Kubernetes Service (AKS)?][what-is-aks] + +## Contributors + +*This article is maintained by Microsoft. It was originally written by the following contributors*: + +* Ken Kilty | Principal TPM +* Russell de Pina | Principal TPM +* Adrian Joian | Senior Customer Engineer +* Jenny Hayes | Senior Content Developer +* Carol Smith | Senior Content Developer +* Erin Schaffer | Content Developer 2 +* Adam Sharif | Customer Engineer 2 + + +[helm-upgrade]: https://helm.sh/docs/helm/helm_upgrade/ +[create-infrastructure]: ./create-postgresql-ha.md +[kubectl-create-secret]: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_create/kubectl_create_secret/ +[kubectl-get]: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_get/ +[kubectl-apply]: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_apply/ +[helm-repo-add]: https://helm.sh/docs/helm/helm_repo_add/ +[az-aks-show]: /cli/azure/aks#az_aks_show +[az-identity-federated-credential-create]: /cli/azure/identity/federated-credential#az_identity_federated_credential_create +[cluster-crd]: https://cloudnative-pg.io/documentation/1.23/cloudnative-pg.v1/#postgresql-cnpg-io-v1-ClusterSpec +[kubectl-describe]: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_describe/ +[az-storage-blob-list]: /cli/azure/storage/blob/#az_storage_blob_list +[az-identity-federated-credential-delete]: /cli/azure/identity/federated-credential#az_identity_federated_credential_delete +[kubectl-delete]: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_delete/ +[az-group-delete]: /cli/azure/group#az_group_delete +[what-is-aks]: ./what-is-aks.md \ No newline at end of file diff --git a/scenarios/azure-aks-docs/articles/aks/postgresql-ha-overview.md b/scenarios/azure-aks-docs/articles/aks/postgresql-ha-overview.md new file mode 100644 index 000000000..455d6024e --- /dev/null +++ b/scenarios/azure-aks-docs/articles/aks/postgresql-ha-overview.md @@ -0,0 +1,92 @@ +--- +title: 'Overview of deploying a highly available PostgreSQL database on AKS with Azure CLI' +description: Learn how to deploy a highly available PostgreSQL database on AKS using the CloudNativePG operator. +ms.topic: overview +ms.date: 06/07/2024 +author: kenkilty +ms.author: kkilty +ms.custom: innovation-engine, aks-related-content +#Customer intent: As a developer or cluster operator, I want to deploy a highly available PostgreSQL database on AKS so I can see how to run a stateful database workload using the managed Kubernetes service in Azure. +--- +# Deploy a highly available PostgreSQL database on AKS with Azure CLI + +In this guide, you deploy a highly available PostgreSQL cluster that spans multiple Azure availability zones on AKS with Azure CLI. + +This article walks through the prerequisites for setting up a PostgreSQL cluster on [Azure Kubernetes Service (AKS)][what-is-aks] and provides an overview of the full deployment process and architecture. + +[!INCLUDE [open source disclaimer](./includes/open-source-disclaimer.md)] + +## Prerequisites + +* This guide assumes a basic understanding of [core Kubernetes concepts][core-kubernetes-concepts] and [PostgreSQL][postgresql]. +* You need the **Owner** or **User Access Administrator** and the **Contributor** [Azure built-in roles][azure-roles] on a subscription in your Azure account. + +[!INCLUDE [azure-cli-prepare-your-environment-no-header.md](~/reusable-content/azure-cli/azure-cli-prepare-your-environment-no-header.md)] + +* You also need the following resources installed: + + * [Azure CLI](/cli/azure/install-azure-cli) version 2.56 or later. + * [Azure Kubernetes Service (AKS) preview extension][aks-preview]. + * [jq][jq], version 1.5 or later. + * [kubectl][install-kubectl] version 1.21.0 or later. + * [Helm][install-helm] version 3.0.0 or later. + * [openssl][install-openssl] version 3.3.0 or later. + * [Visual Studio Code][install-vscode] or equivalent. + * [Krew][install-krew] version 0.4.4 or later. + * [kubectl CloudNativePG (CNPG) Plugin][cnpg-plugin]. + +## Deployment process + +In this guide, you learn how to: + +* Use Azure CLI to create a multi-zone AKS cluster. +* Deploy a highly available PostgreSQL cluster and database using the [CNPG operator][cnpg-plugin]. +* Set up monitoring for PostgreSQL using Prometheus and Grafana. +* Deploy a sample dataset to a PostgreSQL database. +* Perform PostgreSQL and AKS cluster upgrades. +* Simulate a cluster interruption and PostgreSQL replica failover. +* Perform backup and restore of a PostgreSQL database. + +## Deployment architecture + +This diagram illustrates a PostgreSQL cluster setup with one primary replica and two read replicas managed by the [CloudNativePG (CNPG)](https://cloudnative-pg.io/) operator. The architecture provides a highly available PostgreSQL running on an AKS cluster that can withstand a zone outage by failing over across replicas. + +Backups are stored on [Azure Blob Storage](/azure/storage/blobs/), providing another way to restore the database in the event of an issue with streaming replication from the primary replica. + +:::image source="./media/postgresql-ha-overview/postgres-architecture-diagram.png" alt-text="Diagram of CNPG architecture." lightbox="./media/postgresql-ha-overview/postgres-architecture-diagram.png"::: + +> [!NOTE] +> For applications that require data separation at the database level, you can add more databases with postInitSQL commands and similar. It is not currently possible with the CNPG operator to add more databases in a declarative way. +[Learn more](https://github.com/cloudnative-pg/cloudnative-pg) about the CNPG operator. + +## Next steps + +> [!div class="nextstepaction"] +> [Create the infrastructure to deploy a highly available PostgreSQL database on AKS using the CNPG operator][create-infrastructure] + +## Contributors + +*This article is maintained by Microsoft. It was originally written by the following contributors*: + +* Ken Kilty | Principal TPM +* Russell de Pina | Principal TPM +* Adrian Joian | Senior Customer Engineer +* Jenny Hayes | Senior Content Developer +* Carol Smith | Senior Content Developer +* Erin Schaffer | Content Developer 2 +* Adam Sharif | Customer Engineer 2 + + +[what-is-aks]: ./what-is-aks.md +[postgresql]: https://www.postgresql.org/ +[core-kubernetes-concepts]: ./concepts-clusters-workloads.md +[azure-roles]: /azure/role-based-access-control/built-in-roles +[aks-preview]: ./draft.md#install-the-aks-preview-azure-cli-extension +[jq]: https://jqlang.github.io/jq/ +[install-kubectl]: https://kubernetes.io/docs/tasks/tools/install-kubectl/ +[install-helm]: https://helm.sh/docs/intro/install/ +[install-openssl]: https://www.openssl.org/ +[install-vscode]: https://code.visualstudio.com/Download +[install-krew]: https://krew.sigs.k8s.io/ +[cnpg-plugin]: https://cloudnative-pg.io/documentation/current/kubectl-plugin/#using-krew +[create-infrastructure]: ./create-postgresql-ha.md \ No newline at end of file diff --git a/scenarios/azure-aks-docs/articles/aks/trusted-access-feature.md b/scenarios/azure-aks-docs/articles/aks/trusted-access-feature.md new file mode 100644 index 000000000..9922a8ad4 --- /dev/null +++ b/scenarios/azure-aks-docs/articles/aks/trusted-access-feature.md @@ -0,0 +1,130 @@ +--- +title: Get secure resource access to Azure Kubernetes Service (AKS) using Trusted Access +description: Learn how to use the Trusted Access feature to give Azure resources access to Azure Kubernetes Service (AKS) clusters. +author: schaffererin +ms.topic: how-to +ms.custom: devx-track-azurecli, innovation-engine +ms.date: 11/05/2024 +ms.author: schaffererin +--- + +# Get secure access for Azure resources in Azure Kubernetes Service by using Trusted Access + +This article shows you how to get secure access for your Azure services to your Kubernetes API server in Azure Kubernetes Service (AKS) using Trusted Access. + +The Trusted Access feature gives services secure access to AKS API server by using the Azure back end without requiring a private endpoint. Instead of relying on identities that have [Microsoft Entra](/azure/active-directory/fundamentals/active-directory-whatis) permissions, this feature can use your system-assigned managed identity to authenticate with the managed services and applications that you want to use with your AKS clusters. + +> [!NOTE] +> The Trusted Access API is generally available. We provide general availability (GA) support for the Azure CLI, but it's still in preview and requires using the aks-preview extension. + +## Trusted Access feature overview + +Trusted Access addresses the following scenarios: + +* If an authorized IP range is set or in a private cluster, Azure services might not be able to access the Kubernetes API server unless you implement a private endpoint access model. +* Giving an Azure service admin access to the Kubernetes API doesn't follow the least privilege access best practice and can lead to privilege escalations or risk of credentials leakage. For example, you might have to implement high-privileged service-to-service permissions, and they aren't ideal in an audit review. + +You can use Trusted Access to give explicit consent to your system-assigned managed identity of allowed resources to access your AKS clusters by using an Azure resource called a *role binding*. Your Azure resources access AKS clusters through the AKS regional gateway via system-assigned managed identity authentication. The appropriate Kubernetes permissions are assigned via an Azure resource called a *role*. Through Trusted Access, you can access AKS clusters with different configurations including but not limited to [private clusters](private-clusters.md), [clusters that have local accounts turned off](manage-local-accounts-managed-azure-ad.md#disable-local-accounts), [Microsoft Entra clusters](azure-ad-integration-cli.md), and [authorized IP range clusters](api-server-authorized-ip-ranges.md). + +## Prerequisites + +* An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). +* Resource types that support [system-assigned managed identity](/azure/active-directory/managed-identities-azure-resources/overview). +* Azure CLI version 2.53.0 or later. Run `az --version` to find your version. If you need to install or upgrade, see [Install Azure CLI][azure-cli-install]. +* To learn what roles to use in different scenarios, see these articles: + * [Azure Machine Learning access to AKS clusters with special configurations](https://github.com/Azure/AML-Kubernetes/blob/master/docs/azureml-aks-ta-support.md) + * [What is Azure Kubernetes Service backup?][aks-azure-backup] + * [Turn on an agentless container posture](/azure/defender-for-cloud/concept-agentless-containers) +* In the same subscription as the Azure resource that you want to access the cluster, [create an AKS cluster](tutorial-kubernetes-deploy-cluster.md). + +## Connect to your cluster + +Configure `kubectl` to connect to your cluster using the [`az aks get-credentials`][az-aks-get-credentials] command. + +```azurecli-interactive +export RESOURCE_GROUP_NAME="myAKSResourceGroup0b090b" +export CLUSTER_NAME="myAKSCluster0b090b" + +az aks get-credentials --resource-group ${RESOURCE_GROUP_NAME} --name ${CLUSTER_NAME} --overwrite-existing +``` + +Verify the connection to your cluster using the `kubectl get` command. + +```bash +kubectl get nodes +``` + +## Select the required Trusted Access roles + +The roles that you select depend on the Azure services that you want to access the AKS cluster. Azure services help create roles and role bindings that build the connection from the Azure service to AKS. + +To find the roles that you need, see the documentation for the Azure service that you want to connect to AKS. You can also use the Azure CLI to list the roles that are available for the Azure service using the `az aks trustedaccess role list --location ` command. + +## Create a Trusted Access role binding + +After you confirm which role to use, use the Azure CLI to create a Trusted Access role binding in the AKS cluster. The role binding associates your selected role with the Azure service + +```azurecli-interactive +export RESOURCE_GROUP_NAME="myAKSResourceGroup0b090b" +export CLUSTER_NAME="myAKSCluster0b090b" +export RANDOM_SUFFIX=$(openssl rand -hex 3) +export ROLE_BINDING_NAME="myRoleBindingName${RANDOM_SUFFIX}" +export SOURCE_RESOURCE_ID=$(az aks show --resource-group $RESOURCE_GROUP_NAME --name $CLUSTER_NAME --query id --output tsv) +export ROLE_NAME_1="Microsoft.ContainerService/managedClusters/roleName1" +export ROLE_NAME_2="Microsoft.ContainerService/managedClusters/roleName2" + +az aks trustedaccess rolebinding create --resource-group ${RESOURCE_GROUP_NAME} --cluster-name ${CLUSTER_NAME} --name ${ROLE_BINDING_NAME} --source-resource-id ${SOURCE_RESOURCE_ID} --roles ${ROLE_NAME_1},${ROLE_NAME_2} +``` + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/${RESOURCE_GROUP_NAME}/providers/Microsoft.ContainerService/managedClusters/${CLUSTER_NAME}/trustedAccessRoleBindings/${ROLE_BINDING_NAME}", + "name": "${ROLE_BINDING_NAME}", + "provisioningState": "Succeeded", + "resourceGroup": "${RESOURCE_GROUP_NAME}", + "roles": [ + "${ROLE_NAME_1}", + "${ROLE_NAME_2}" + ], + "sourceResourceId": "${SOURCE_RESOURCE_ID}", + "systemData": null, + "type": "Microsoft.ContainerService/managedClusters/trustedAccessRoleBindings" +} +``` + +## Update an existing Trusted Access role binding + +For an existing role binding that has an associated source service, you can update the role binding with new roles using the `az aks trustedaccess rolebinding update --resource-group $RESOURCE_GROUP_NAME --cluster-name $CLUSTER_NAME --name $ROLE_BINDING_NAME --roles $ROLE_NAME_3,$ROLE_NAME_4` command. This command updates the role binding with the new roles that you specify. + +> [!NOTE] +> The add-on manager updates clusters every five minutes, so the new role binding might take up to five minutes to take effect. Before the new role binding takes effect, the existing role binding still works. +> +> You can use the `az aks trusted access rolebinding list` command to check the current role binding. + +## Show a Trusted Access role binding + +Show a specific Trusted Access role binding using the `az aks trustedaccess rolebinding show --name $ROLE_BINDING_NAME --resource-group $RESOURCE_GROUP_NAME --cluster-name $CLUSTER_NAME` command. + +## List all the Trusted Access role bindings for a cluster + +List all the Trusted Access role bindings for a cluster using the `az aks trustedaccess rolebinding list --resource-group $RESOURCE_GROUP_NAME --cluster-name $CLUSTER_NAME` command. + +## Related content + +* [Deploy and manage cluster extensions for AKS](cluster-extensions.md) +* [Deploy the Azure Machine Learning extension on an AKS or Azure Arc–enabled Kubernetes cluster](/azure/machine-learning/how-to-deploy-kubernetes-extension) +* [Deploy Azure Backup on an AKS cluster](/azure/backup/azure-kubernetes-service-backup-overview) +* [Set agentless container posture in Microsoft Defender for Cloud for an AKS cluster](/azure/defender-for-cloud/concept-agentless-containers) + + + +[az-feature-register]: /cli/azure/feature#az-feature-register +[az-feature-show]: /cli/azure/feature#az-feature-show +[az-provider-register]: /cli/azure/provider#az-provider-register +[aks-azure-backup]: /azure/backup/azure-kubernetes-service-backup-overview +[azure-cli-install]: /cli/azure/install-azure-cli +[az-aks-get-credentials]: /cli/azure/aks#az-aks-get-credentials \ No newline at end of file diff --git a/scenarios/azure-aks-docs/articles/aks/workload-identity-deploy-cluster.md b/scenarios/azure-aks-docs/articles/aks/workload-identity-deploy-cluster.md new file mode 100644 index 000000000..df6635ce0 --- /dev/null +++ b/scenarios/azure-aks-docs/articles/aks/workload-identity-deploy-cluster.md @@ -0,0 +1,398 @@ +--- +title: Deploy and configure an AKS cluster with workload identity +description: In this Azure Kubernetes Service (AKS) article, you deploy an Azure Kubernetes Service cluster and configure it with a Microsoft Entra Workload ID. +author: tamram +ms.topic: how-to +ms.subservice: aks-security +ms.custom: devx-track-azurecli, innovation-engine +ms.date: 05/28/2024 +ms.author: tamram +--- + +# Deploy and configure workload identity on an Azure Kubernetes Service (AKS) cluster + +Azure Kubernetes Service (AKS) is a managed Kubernetes service that lets you quickly deploy and manage Kubernetes clusters. This article shows you how to: + +* Deploy an AKS cluster using the Azure CLI with the OpenID Connect issuer and a Microsoft Entra Workload ID. +* Create a Microsoft Entra Workload ID and Kubernetes service account. +* Configure the managed identity for token federation. +* Deploy the workload and verify authentication with the workload identity. +* Optionally grant a pod in the cluster access to secrets in an Azure key vault. + +This article assumes you have a basic understanding of Kubernetes concepts. For more information, see [Kubernetes core concepts for Azure Kubernetes Service (AKS)][kubernetes-concepts]. If you aren't familiar with Microsoft Entra Workload ID, see the following [Overview][workload-identity-overview] article. + +## Prerequisites + +* [!INCLUDE [quickstarts-free-trial-note](~/reusable-content/ce-skilling/azure/includes/quickstarts-free-trial-note.md)] +* This article requires version 2.47.0 or later of the Azure CLI. If using Azure Cloud Shell, the latest version is already installed. +* Make sure that the identity that you're using to create your cluster has the appropriate minimum permissions. For more information about access and identity for AKS, see [Access and identity options for Azure Kubernetes Service (AKS)][aks-identity-concepts]. +* If you have multiple Azure subscriptions, select the appropriate subscription ID in which the resources should be billed using the [az account set][az-account-set] command. + +> [!NOTE] +> You can use _Service Connector_ to help you configure some steps automatically. See also: [Tutorial: Connect to Azure storage account in Azure Kubernetes Service (AKS) with Service Connector using workload identity][tutorial-python-aks-storage-workload-identity]. + +## Create a resource group + +An [Azure resource group][azure-resource-group] is a logical group in which Azure resources are deployed and managed. When you create a resource group, you're prompted to specify a location. This location is the storage location of your resource group metadata and where your resources run in Azure if you don't specify another region during resource creation. + +Create a resource group by calling the [az group create][az-group-create] command: + +```azurecli-interactive +export RANDOM_ID="$(openssl rand -hex 3)" +export RESOURCE_GROUP="myResourceGroup$RANDOM_ID" +export REGION="centralindia" +az group create --name "${RESOURCE_GROUP}" --location "${REGION}" +``` + +The following output example shows successful creation of a resource group: + +Results: + +```json +{ + "id": "/subscriptions//resourceGroups/myResourceGroup", + "location": "eastus", + "managedBy": null, + "name": "myResourceGroup", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Create an AKS cluster + +Create an AKS cluster using the [az aks create][az-aks-create] command with the `--enable-oidc-issuer` parameter to enable the OIDC issuer. The following example creates a cluster with a single node: + +```azurecli-interactive +export CLUSTER_NAME="myAKSCluster$RANDOM_ID" +az aks create \ + --resource-group "${RESOURCE_GROUP}" \ + --name "${CLUSTER_NAME}" \ + --enable-oidc-issuer \ + --enable-workload-identity \ + --generate-ssh-keys +``` + +After a few minutes, the command completes and returns JSON-formatted information about the cluster. + +> [!NOTE] +> When you create an AKS cluster, a second resource group is automatically created to store the AKS resources. For more information, see [Why are two resource groups created with AKS?][aks-two-resource-groups]. + +## Update an existing AKS cluster + +You can update an AKS cluster to use the OIDC issuer and enable workload identity by calling the [az aks update][az aks update] command with the `--enable-oidc-issuer` and the `--enable-workload-identity` parameters. + +## Retrieve the OIDC issuer URL + +To get the OIDC issuer URL and save it to an environmental variable, run the following command: + +```azurecli-interactive +export AKS_OIDC_ISSUER="$(az aks show --name "${CLUSTER_NAME}" \ + --resource-group "${RESOURCE_GROUP}" \ + --query "oidcIssuerProfile.issuerUrl" \ + --output tsv)" +``` + +The environment variable should contain the issuer URL, similar to the following example: + +```output +https://eastus.oic.prod-aks.azure.com/00000000-0000-0000-0000-000000000000/11111111-1111-1111-1111-111111111111/ +``` + +By default, the issuer is set to use the base URL `https://{region}.oic.prod-aks.azure.com/{tenant_id}/{uuid}`, where the value for `{region}` matches the location to which the AKS cluster is deployed. The value `{uuid}` represents the OIDC key, which is a randomly generated guid for each cluster that is immutable. + +## Create a managed identity + +Call the [az identity create][az-identity-create] command to create a managed identity. + +```azurecli-interactive +export SUBSCRIPTION="$(az account show --query id --output tsv)" +export USER_ASSIGNED_IDENTITY_NAME="myIdentity$RANDOM_ID" +az identity create \ + --name "${USER_ASSIGNED_IDENTITY_NAME}" \ + --resource-group "${RESOURCE_GROUP}" \ + --location "${REGION}" \ + --subscription "${SUBSCRIPTION}" +``` + +The following output example shows successful creation of a managed identity: + +Results: + +```output +{ + "clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroupxxxxxx/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentityxxxxxx", + "location": "centralindia", + "name": "myIdentityxxxxxx", + "principalId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "resourceGroup": "myResourceGroupxxxxxx", + "systemData": null, + "tags": {}, + "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "type": "Microsoft.ManagedIdentity/userAssignedIdentities" +} +``` + +Next, create a variable for the managed identity's client ID. + +```azurecli-interactive +export USER_ASSIGNED_CLIENT_ID="$(az identity show \ + --resource-group "${RESOURCE_GROUP}" \ + --name "${USER_ASSIGNED_IDENTITY_NAME}" \ + --query 'clientId' \ + --output tsv)" +``` + +## Create a Kubernetes service account + +Create a Kubernetes service account and annotate it with the client ID of the managed identity created in the previous step. Use the [az aks get-credentials][az-aks-get-credentials] command and replace the values for the cluster name and the resource group name. + +```azurecli-interactive +az aks get-credentials --name "${CLUSTER_NAME}" --resource-group "${RESOURCE_GROUP}" +``` + +Copy and paste the following multi-line input in the Azure CLI. + +```azurecli-interactive +export SERVICE_ACCOUNT_NAMESPACE="default" +export SERVICE_ACCOUNT_NAME="workload-identity-sa$RANDOM_ID" +cat < [!NOTE] +> It takes a few seconds for the federated identity credential to propagate after it is added. If a token request is made immediately after adding the federated identity credential, the request might fail until the cache is refreshed. To avoid this issue, you can add a slight delay after adding the federated identity credential. + +## Deploy your application + +When you deploy your application pods, the manifest should reference the service account created in the **Create Kubernetes service account** step. The following manifest shows how to reference the account, specifically the _metadata\namespace_ and _spec\serviceAccountName_ properties. Make sure to specify an image for `` and a container name for ``: + +```bash +cat < [!IMPORTANT] +> Ensure that the application pods using workload identity include the label `azure.workload.identity/use: "true"` in the pod spec. Otherwise the pods will fail after they are restarted. + +## Grant permissions to access Azure Key Vault + +The instructions in this step show how to access secrets, keys, or certificates in an Azure key vault from the pod. The examples in this section configure access to secrets in the key vault for the workload identity, but you can perform similar steps to configure access to keys or certificates. + +The following example shows how to use the Azure role-based access control (Azure RBAC) permission model to grant the pod access to the key vault. For more information about the Azure RBAC permission model for Azure Key Vault, see [Grant permission to applications to access an Azure key vault using Azure RBAC](/azure/key-vault/general/rbac-guide). + +1. Create a key vault with purge protection and RBAC authorization enabled. You can also use an existing key vault if it is configured for both purge protection and RBAC authorization: + + ```azurecli-interactive + export KEYVAULT_NAME="keyvault-workload-id$RANDOM_ID" + # Ensure the key vault name is between 3-24 characters + if [ ${#KEYVAULT_NAME} -gt 24 ]; then + KEYVAULT_NAME="${KEYVAULT_NAME:0:24}" + fi + az keyvault create \ + --name "${KEYVAULT_NAME}" \ + --resource-group "${RESOURCE_GROUP}" \ + --location "${REGION}" \ + --enable-purge-protection \ + --enable-rbac-authorization + ``` + +1. Assign yourself the RBAC [Key Vault Secrets Officer](/azure/role-based-access-control/built-in-roles/security#key-vault-secrets-officer) role so that you can create a secret in the new key vault: + + ```azurecli-interactive + export KEYVAULT_RESOURCE_ID=$(az keyvault show --resource-group "${KEYVAULT_RESOURCE_GROUP}" \ + --name "${KEYVAULT_NAME}" \ + --query id \ + --output tsv) + + export CALLER_OBJECT_ID=$(az ad signed-in-user show --query id -o tsv) + + az role assignment create --assignee "${CALLER_OBJECT_ID}" \ + --role "Key Vault Secrets Officer" \ + --scope "${KEYVAULT_RESOURCE_ID}" + ``` + +1. Create a secret in the key vault: + + ```azurecli-interactive + export KEYVAULT_SECRET_NAME="my-secret$RANDOM_ID" + az keyvault secret set \ + --vault-name "${KEYVAULT_NAME}" \ + --name "${KEYVAULT_SECRET_NAME}" \ + --value "Hello\!" + ``` + +1. Assign the [Key Vault Secrets User](/azure/role-based-access-control/built-in-roles/security#key-vault-secrets-user) role to the user-assigned managed identity that you created previously. This step gives the managed identity permission to read secrets from the key vault: + + ```azurecli-interactive + export IDENTITY_PRINCIPAL_ID=$(az identity show \ + --name "${USER_ASSIGNED_IDENTITY_NAME}" \ + --resource-group "${RESOURCE_GROUP}" \ + --query principalId \ + --output tsv) + + az role assignment create \ + --assignee-object-id "${IDENTITY_PRINCIPAL_ID}" \ + --role "Key Vault Secrets User" \ + --scope "${KEYVAULT_RESOURCE_ID}" \ + --assignee-principal-type ServicePrincipal + ``` + +1. Create an environment variable for the key vault URL: + + ```azurecli-interactive + export KEYVAULT_URL="$(az keyvault show \ + --resource-group ${RESOURCE_GROUP} \ + --name ${KEYVAULT_NAME} \ + --query properties.vaultUri \ + --output tsv)" + ``` + +1. Deploy a pod that references the service account and key vault URL: + + ```bash + kubectl apply -f - < [!IMPORTANT] +> Azure RBAC role assignments can take up to ten minutes to propagate. If the pod is unable to access the secret, you may need to wait for the role assignment to propagate. For more information, see [Troubleshoot Azure RBAC](/azure/role-based-access-control/troubleshooting#). + +## Disable workload identity + +To disable the Microsoft Entra Workload ID on the AKS cluster where it's been enabled and configured, update the AKS cluster by setting the `--disable-workload-identity` parameter using the `az aks update` command. + +## Next steps + +In this article, you deployed a Kubernetes cluster and configured it to use a workload identity in preparation for application workloads to authenticate with that credential. Now you're ready to deploy your application and configure it to use the workload identity with the latest version of the [Azure Identity][azure-identity-libraries] client library. If you can't rewrite your application to use the latest client library version, you can [set up your application pod][workload-identity-migration] to authenticate using managed identity with workload identity as a short-term migration solution. + +The [Service Connector](/azure/service-connector/overview) integration helps simplify the connection configuration for AKS workloads and Azure backing services. It securely handles authentication and network configurations and follows best practices for connecting to Azure services. For more information, see [Connect to Azure OpenAI Service in AKS using Workload Identity](/azure/service-connector/tutorial-python-aks-openai-workload-identity) and the [Service Connector introduction](https://azure.github.io/AKS/2024/05/23/service-connector-intro). + + +[kubectl-describe]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#describe + + +[kubernetes-concepts]: concepts-clusters-workloads.md +[workload-identity-overview]: workload-identity-overview.md +[azure-resource-group]: /azure/azure-resource-manager/management/overview +[az-group-create]: /cli/azure/group#az-group-create +[aks-identity-concepts]: concepts-identity.md +[federated-identity-credential]: /graph/api/resources/federatedidentitycredentials-overview +[tutorial-python-aks-storage-workload-identity]: /azure/service-connector/tutorial-python-aks-storage-workload-identity +[az-aks-create]: /cli/azure/aks#az-aks-create +[az aks update]: /cli/azure/aks#az-aks-update +[aks-two-resource-groups]: faq.yml +[az-account-set]: /cli/azure/account#az-account-set +[az-identity-create]: /cli/azure/identity#az-identity-create +[az-aks-get-credentials]: /cli/azure/aks#az-aks-get-credentials +[az-identity-federated-credential-create]: /cli/azure/identity/federated-credential#az-identity-federated-credential-create +[workload-identity-migration]: workload-identity-migrate-from-pod-identity.md +[azure-identity-libraries]: /azure/active-directory/develop/reference-v2-libraries \ No newline at end of file diff --git a/scenarios/azure-aks-docs/articles/aks/workload-identity-migrate-from-pod-identity.md b/scenarios/azure-aks-docs/articles/aks/workload-identity-migrate-from-pod-identity.md new file mode 100644 index 000000000..43c5fd88e --- /dev/null +++ b/scenarios/azure-aks-docs/articles/aks/workload-identity-migrate-from-pod-identity.md @@ -0,0 +1,181 @@ +--- +title: Migrate your Azure Kubernetes Service (AKS) pod to use workload identity +description: In this Azure Kubernetes Service (AKS) article, you learn how to configure your Azure Kubernetes Service pod to authenticate with workload identity. +ms.topic: how-to +ms.subservice: aks-security +ms.custom: devx-track-azurecli, innovation-engine +ms.date: 07/31/2023 +author: nickomang +ms.author: nickoman +--- + +# Migrate from pod managed-identity to workload identity + +## Create resource group +Set your subscription to be the current active subscription using the `az account set` command. Then, create a random suffix to ensure unique resource names. + +```bash +export RANDOM_SUFFIX=$(openssl rand -hex 3) +export RESOURCE_GROUP_NAME="myResourceGroup$RANDOM_SUFFIX" +export LOCATION="WestUS2" +az group create --name "$RESOURCE_GROUP_NAME" --location "$LOCATION" +``` + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxx/resourceGroups/myResourceGroupxxx", + "location": "", + "managedBy": null, + "name": "myResourceGroupxxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Create a managed identity. + +```bash +export IDENTITY_NAME="userAssignedIdentity$RANDOM_SUFFIX" +az identity create --name "$IDENTITY_NAME" --resource-group "$RESOURCE_GROUP_NAME" --location "$LOCATION" +``` + +Results: + + + +```json +{ + "clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "id": "/subscriptions/xxxxx/resourceGroups/myResourceGroupxxx/providers/Microsoft.ManagedIdentity/userAssignedIdentities/userAssignedIdentityxxx", + "location": "", + "name": "userAssignedIdentityxxx", + "principalId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "resourceGroup": "myResourceGroupxxx", + "tags": {}, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities" +} +``` + +## Get Client ID + +Save the client ID of the managed identity to an environment variable. + +```bash +export USER_ASSIGNED_CLIENT_ID="$(az identity show --resource-group "$RESOURCE_GROUP_NAME" --name "$IDENTITY_NAME" --query 'clientId' -o tsv)" +``` + +## Save OIDC Issuer URL +Get the OIDC Issuer URL and save it to an environment variable.By default, the Issuer is set to use the base URL `https://{region}.oic.prod-aks.azure.com/{uuid}`, where the value for `{region}` matches the location the AKS cluster is deployed in. The value `{uuid}` represents the OIDC key. + +```bash +export AKS_CLUSTER_NAME=$MY_AKS_CLUSTER_NAME +export AKS_RESOURCE_GROUP=$MY_AKS_RESOURCE_GROUP +export AKS_OIDC_ISSUER="$(az aks show --name "$AKS_CLUSTER_NAME" --resource-group "$AKS_RESOURCE_GROUP" --query "oidcIssuerProfile.issuerUrl" -o tsv)" +``` + +## Load credentials + +Get the Kubernetes credentials for your cluster. + +```bash +az aks get-credentials --name "$AKS_CLUSTER_NAME" --resource-group "$AKS_RESOURCE_GROUP" +``` + +## Create Namespace + +Create a namespace. + +```bash +export SERVICE_ACCOUNT_NAMESPACE="mynamespace$RANDOM_SUFFIX" +kubectl create namespace "$SERVICE_ACCOUNT_NAMESPACE" +``` + +## Create Service Account +Create the service account and annotate it with the client ID of the managed identity. + +```bash +export SERVICE_ACCOUNT_NAME="myserviceaccount$RANDOM_SUFFIX" +kubectl create serviceaccount "$SERVICE_ACCOUNT_NAME" -n "$SERVICE_ACCOUNT_NAMESPACE" +kubectl annotate serviceaccount "$SERVICE_ACCOUNT_NAME" --namespace "$SERVICE_ACCOUNT_NAMESPACE" azure.workload.identity/client-id="$USER_ASSIGNED_CLIENT_ID" +``` + +## Establish federated identity credential trust + +Establish a federated identity credential between the managed identity, the service account issuer, and the subject. + +```bash +export FEDERATED_CREDENTIAL_NAME="myFederatedCredentialName$RANDOM_SUFFIX" +az identity federated-credential create --name "$FEDERATED_CREDENTIAL_NAME" --identity-name "$IDENTITY_NAME" --resource-group "$RESOURCE_GROUP_NAME" --issuer "$AKS_OIDC_ISSUER" --subject "system:serviceaccount:$SERVICE_ACCOUNT_NAMESPACE:$SERVICE_ACCOUNT_NAME" --audience "api://AzureADTokenExchange" +``` + +## Deploy the workload with migration sidecar + +```bash +export POD_NAME="httpbin-pod" + +cat < pod.yaml +apiVersion: v1 +kind: Pod +metadata: + name: $POD_NAME + namespace: $SERVICE_ACCOUNT_NAMESPACE + labels: + app: httpbin + annotations: + azure.workload.identity/inject-proxy-sidecar: "true" + azure.workload.identity/proxy-sidecar-port: "8000" +spec: + serviceAccountName: $SERVICE_ACCOUNT_NAME + containers: + - name: httpbin + image: docker.io/kennethreitz/httpbin + env: + - name: IDENTITY_ENDPOINT + value: "http://localhost:8000/metadata/identity/oauth2/token" + - name: IDENTITY_HEADER + value: "true" + - name: IMDS_ENDPOINT + value: "http://169.254.169.254" +EOF +kubectl apply -f pod.yaml +kubectl wait --for=condition=Ready pod/httpbin-pod -n "$SERVICE_ACCOUNT_NAMESPACE" --timeout=120s +kubectl describe pods $POD_NAME -n "$SERVICE_ACCOUNT_NAMESPACE" +kubectl logs $POD_NAME -n "$SERVICE_ACCOUNT_NAMESPACE" +``` + +## Remove pod-managed identity + +After you've completed your testing and the application is successfully able to get a token using the proxy sidecar, you can remove the Microsoft Entra pod-managed identity mapping for the pod from your cluster, and then remove the identity. + +```bash +az aks pod-identity delete $IDENTITY_NAME +``` + +## Next steps + +This article showed you how to set up your pod to authenticate using a workload identity as a migration option. For more information about Microsoft Entra Workload ID, see the [Overview][workload-identity-overview] article. + + +[pod-annotations]: workload-identity-overview.md#pod-annotations +[az-identity-create]: /cli/azure/identity#az-identity-create +[az-account-set]: /cli/azure/account#az-account-set +[az-aks-get-credentials]: /cli/azure/aks#az-aks-get-credentials +[workload-identity-overview]: workload-identity-overview.md +[az-identity-federated-credential-create]: /cli/azure/identity/federated-credential#az-identity-federated-credential-create +[az-aks-pod-identity-delete]: /cli/azure/aks/pod-identity#az-aks-pod-identity-delete +[azure-identity-supported-versions]: workload-identity-overview.md#dependencies +[azure-identity-libraries]: ../active-directory/develop/reference-v2-libraries.md +[openid-connect-overview]: /azure/active-directory/develop/v2-protocols-oidc +[install-azure-cli]: /cli/azure/install-azure-cli +[assign-rbac-managed-identity]: /azure/role-based-access-control/role-assignments-portal-managed-identity + + +[kubectl-describe]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#describe +[kubelet-logs]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#logs \ No newline at end of file diff --git a/scenarios/azure-databases-docs/articles/mysql/flexible-server/tutorial-deploy-wordpress-on-aks.md b/scenarios/azure-databases-docs/articles/mysql/flexible-server/tutorial-deploy-wordpress-on-aks.md new file mode 100644 index 000000000..7abe980b7 --- /dev/null +++ b/scenarios/azure-databases-docs/articles/mysql/flexible-server/tutorial-deploy-wordpress-on-aks.md @@ -0,0 +1,488 @@ +--- +title: 'Tutorial: Deploy WordPress on AKS cluster by using Azure CLI' +description: Learn how to quickly build and deploy WordPress on AKS with Azure Database for MySQL - Flexible Server. +ms.service: mysql +ms.subservice: flexible-server +author: mksuni +ms.author: sumuth +ms.topic: tutorial +ms.date: 3/20/2024 +ms.custom: vc, devx-track-azurecli, innovation-engine, linux-related-content +--- + +# Tutorial: Deploy WordPress app on AKS with Azure Database for MySQL - Flexible Server + +[!INCLUDE[applies-to-mysql-flexible-server](../includes/applies-to-mysql-flexible-server.md)] + +[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://go.microsoft.com/fwlink/?linkid=2286232) + +In this tutorial, you deploy a scalable WordPress application secured via HTTPS on an Azure Kubernetes Service (AKS) cluster with Azure Database for MySQL flexible server using the Azure CLI. +**[AKS](../../aks/intro-kubernetes.md)** is a managed Kubernetes service that lets you quickly deploy and manage clusters. **[Azure Database for MySQL flexible server](overview.md)** is a fully managed database service designed to provide more granular control and flexibility over database management functions and configuration settings. + +> [!NOTE] +> This tutorial assumes a basic understanding of Kubernetes concepts, WordPress, and MySQL. + +[!INCLUDE [flexible-server-free-trial-note](../includes/flexible-server-free-trial-note.md)] + +## Prerequisites + +Before you get started, make sure you're logged into Azure CLI and have selected a subscription to use with the CLI. Ensure you have [Helm installed](https://helm.sh/docs/intro/install/). + +> [!NOTE] +> If you're running the commands in this tutorial locally instead of Azure Cloud Shell, run the commands as administrator. + +## Create a resource group + +An Azure resource group is a logical group in which Azure resources are deployed and managed. All resources must be placed in a resource group. The following command creates a resource group with the previously defined `$MY_RESOURCE_GROUP_NAME` and `$REGION` parameters. + +```bash +export RANDOM_ID="$(openssl rand -hex 3)" +export MY_RESOURCE_GROUP_NAME="myWordPressAKSResourceGroup$RANDOM_ID" +export REGION="westeurope" +az group create \ + --name $MY_RESOURCE_GROUP_NAME \ + --location $REGION +``` + +Results: + +```json +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myWordPressAKSResourceGroupXXX", + "location": "eastus", + "managedBy": null, + "name": "testResourceGroup", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +> [!NOTE] +> The location for the resource group is where resource group metadata is stored. It's also where your resources run in Azure if you don't specify another region during resource creation. + +## Create a virtual network and subnet + +A virtual network is the fundamental building block for private networks in Azure. Azure Virtual Network enables Azure resources like VMs to securely communicate with each other and the internet. + +```bash +export NETWORK_PREFIX="$(($RANDOM % 253 + 1))" +export MY_VNET_PREFIX="10.$NETWORK_PREFIX.0.0/16" +export MY_SN_PREFIX="10.$NETWORK_PREFIX.0.0/22" +export MY_VNET_NAME="myVNet$RANDOM_ID" +export MY_SN_NAME="mySN$RANDOM_ID" +az network vnet create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --location $REGION \ + --name $MY_VNET_NAME \ + --address-prefix $MY_VNET_PREFIX \ + --subnet-name $MY_SN_NAME \ + --subnet-prefixes $MY_SN_PREFIX +``` + +Results: + +```json +{ + "newVNet": { + "addressSpace": { + "addressPrefixes": [ + "10.210.0.0/16" + ] + }, + "enableDdosProtection": false, + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/myWordPressAKSResourceGroupXXX/providers/Microsoft.Network/virtualNetworks/myVNetXXX", + "location": "eastus", + "name": "myVNet210", + "provisioningState": "Succeeded", + "resourceGroup": "myWordPressAKSResourceGroupXXX", + "subnets": [ + { + "addressPrefix": "10.210.0.0/22", + "delegations": [], + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/myWordPressAKSResourceGroupXXX/providers/Microsoft.Network/virtualNetworks/myVNetXXX/subnets/mySNXXX", + "name": "mySN210", + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled", + "provisioningState": "Succeeded", + "resourceGroup": "myWordPressAKSResourceGroupXXX", + "type": "Microsoft.Network/virtualNetworks/subnets" + } + ], + "type": "Microsoft.Network/virtualNetworks", + "virtualNetworkPeerings": [] + } +} +``` + +## Create an Azure Database for MySQL flexible server instance + +Azure Database for MySQL flexible server is a managed service that you can use to run, manage, and scale highly available MySQL servers in the cloud. Create an Azure Database for MySQL flexible server instance with the [az mysql flexible-server create](/cli/azure/mysql/flexible-server) command. A server can contain multiple databases. The following command creates a server using service defaults and variable values from your Azure CLI's local context: + +```bash +export MY_DNS_LABEL="mydnslabel$RANDOM_ID" +export MY_MYSQL_DB_NAME="mydb$RANDOM_ID" +export MY_MYSQL_ADMIN_USERNAME="dbadmin$RANDOM_ID" +export MY_MYSQL_ADMIN_PW="$(openssl rand -base64 32)" +export MY_MYSQL_SN_NAME="myMySQLSN$RANDOM_ID" +az mysql flexible-server create \ + --admin-password $MY_MYSQL_ADMIN_PW \ + --admin-user $MY_MYSQL_ADMIN_USERNAME \ + --auto-scale-iops Disabled \ + --high-availability Disabled \ + --iops 500 \ + --location $REGION \ + --name $MY_MYSQL_DB_NAME \ + --database-name wordpress \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --sku-name Standard_B2s \ + --storage-auto-grow Disabled \ + --storage-size 20 \ + --subnet $MY_MYSQL_SN_NAME \ + --private-dns-zone $MY_DNS_LABEL.private.mysql.database.azure.com \ + --tier Burstable \ + --version 8.0.21 \ + --vnet $MY_VNET_NAME \ + --yes -o JSON +``` + +Results: + +```json +{ + "databaseName": "wordpress", + "host": "mydbxxx.mysql.database.azure.com", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myWordPressAKSResourceGroupXXX/providers/Microsoft.DBforMySQL/flexibleServers/mydbXXX", + "location": "East US", + "resourceGroup": "myWordPressAKSResourceGroupXXX", + "skuname": "Standard_B2s", + "subnetId": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myWordPressAKSResourceGroupXXX/providers/Microsoft.Network/virtualNetworks/myVNetXXX/subnets/myMySQLSNXXX", + "username": "dbadminxxx", + "version": "8.0.21" +} +``` + +The server created has the following attributes: + +- A new empty database is created when the server is first provisioned. +- The server name, admin username, admin password, resource group name, and location are already specified in the local context environment of the cloud shell and are in the same location as your resource group and other Azure components. +- The service defaults for the remaining server configurations are compute tier (Burstable), compute size/SKU (Standard_B2s), backup retention period (seven days), and MySQL version (8.0.21). +- The default connectivity method is Private access (virtual network integration) with a linked virtual network and an auto generated subnet. + +> [!NOTE] +> The connectivity method cannot be changed after creating the server. For example, if you selected `Private access (VNet Integration)` during creation, then you cannot change to `Public access (allowed IP addresses)` after creation. We highly recommend creating a server with Private access to securely access your server using VNet Integration. Learn more about Private access in the [concepts article](./concepts-networking-vnet.md). + +If you'd like to change any defaults, refer to the Azure CLI [reference documentation](/cli/azure//mysql/flexible-server) for the complete list of configurable CLI parameters. + +## Check the Azure Database for MySQL - Flexible Server status + +It takes a few minutes to create the Azure Database for MySQL - Flexible Server and supporting resources. + +```bash +runtime="10 minute"; endtime=$(date -ud "$runtime" +%s); while [[ $(date -u +%s) -le $endtime ]]; do STATUS=$(az mysql flexible-server show -g $MY_RESOURCE_GROUP_NAME -n $MY_MYSQL_DB_NAME --query state -o tsv); echo $STATUS; if [ "$STATUS" = 'Ready' ]; then break; else sleep 10; fi; done +``` + +## Configure server parameters in Azure Database for MySQL - Flexible Server + +You can manage Azure Database for MySQL - Flexible Server configuration using server parameters. The server parameters are configured with the default and recommended value when you create the server. + +To show details about a particular parameter for a server, run the [az mysql flexible-server parameter show](/cli/azure/mysql/flexible-server/parameter) command. + +### Disable Azure Database for MySQL - Flexible Server SSL connection parameter for WordPress integration + +You can also modify the value of certain server parameters to update the underlying configuration values for the MySQL server engine. To update the server parameter, use the [az mysql flexible-server parameter set](/cli/azure/mysql/flexible-server/parameter#az-mysql-flexible-server-parameter-set) command. + +```bash +az mysql flexible-server parameter set \ + -g $MY_RESOURCE_GROUP_NAME \ + -s $MY_MYSQL_DB_NAME \ + -n require_secure_transport -v "OFF" -o JSON +``` + +Results: + +```json +{ + "allowedValues": "ON,OFF", + "currentValue": "OFF", + "dataType": "Enumeration", + "defaultValue": "ON", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myWordPressAKSResourceGroupXXX/providers/Microsoft.DBforMySQL/flexibleServers/mydbXXX/configurations/require_secure_transport", + "isConfigPendingRestart": "False", + "isDynamicConfig": "True", + "isReadOnly": "False", + "name": "require_secure_transport", + "resourceGroup": "myWordPressAKSResourceGroupXXX", + "source": "user-override", + "systemData": null, + "type": "Microsoft.DBforMySQL/flexibleServers/configurations", + "value": "OFF" +} +``` + +## Create AKS cluster + +To create an AKS cluster with Container Insights, use the [az aks create](/cli/azure/aks#az-aks-create) command with the **--enable-addons** monitoring parameter. The following example creates an autoscaling, availability zone-enabled cluster named **myAKSCluster**: + +This action takes a few minutes. + +```bash +export MY_SN_ID=$(az network vnet subnet list --resource-group $MY_RESOURCE_GROUP_NAME --vnet-name $MY_VNET_NAME --query "[0].id" --output tsv) +export MY_AKS_CLUSTER_NAME="myAKSCluster$RANDOM_ID" + +az aks create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_AKS_CLUSTER_NAME \ + --auto-upgrade-channel stable \ + --enable-cluster-autoscaler \ + --enable-addons monitoring \ + --location $REGION \ + --node-count 1 \ + --min-count 1 \ + --max-count 3 \ + --network-plugin azure \ + --network-policy azure \ + --vnet-subnet-id $MY_SN_ID \ + --no-ssh-key \ + --node-vm-size Standard_DS2_v2 \ + --service-cidr 10.255.0.0/24 \ + --dns-service-ip 10.255.0.10 \ + --zones 1 2 3 +``` +> [!NOTE] +> When creating an AKS cluster, a second resource group is automatically created to store the AKS resources. See [Why are two resource groups created with AKS?](../../aks/faq.md#why-are-two-resource-groups-created-with-aks) + +## Connect to the cluster + +To manage a Kubernetes cluster, use [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/), the Kubernetes command-line client. If you use Azure Cloud Shell, `kubectl` is already installed. The following example installs `kubectl` locally using the [az aks install-cli](/cli/azure/aks#az-aks-install-cli) command. + + ```bash + if ! [ -x "$(command -v kubectl)" ]; then az aks install-cli; fi +``` + +## Load credentials + +Next, configure `kubectl` to connect to your Kubernetes cluster using the [az aks get-credentials](/cli/azure/aks#az-aks-get-credentials) command. This command downloads credentials and configures the Kubernetes CLI to use them. The command uses `~/.kube/config`, the default location for the [Kubernetes configuration file](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/). You can specify a different location for your Kubernetes configuration file using the **--file** argument. + +```bash +az aks get-credentials --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_AKS_CLUSTER_NAME --overwrite-existing +``` + +## Verify Connection +To verify the connection to your cluster, use the [kubectl get]( https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#get) command to return a list of the cluster nodes. + +```bash +kubectl get nodes +``` + +## Setup FQDN + +You can configure your ingress controller with a static public IP address. The static public IP address remains if you delete your ingress controller. The IP address doesn't remain if you delete your AKS cluster. +When you upgrade your ingress controller, you must pass a parameter to the Helm release to ensure the ingress controller service is made aware of the load balancer that will be allocated to it. For the HTTPS certificates to work correctly, use a DNS label to configure a fully qualified domain name (FQDN) for the ingress controller IP address. Your FQDN should follow this form: $MY_DNS_LABEL.AZURE_REGION_NAME.cloudapp.azure.com. + +```bash +export MY_PUBLIC_IP_NAME="myPublicIP$RANDOM_ID" +export MY_STATIC_IP=$(az network public-ip create --resource-group MC_${MY_RESOURCE_GROUP_NAME}_${MY_AKS_CLUSTER_NAME}_${REGION} --location ${REGION} --name ${MY_PUBLIC_IP_NAME} --dns-name ${MY_DNS_LABEL} --sku Standard --allocation-method static --version IPv4 --zone 1 2 3 --query publicIp.ipAddress -o tsv) +``` + +## Install NGINX ingress controller + +Next, you add the ingress-nginx Helm repository, update the local Helm Chart repository cache, and install ingress-nginx addon via Helm. You can set the DNS label with the **--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-dns-label-name"=""** parameter either when you first deploy the ingress controller or later. In this example, you specify your own public IP address that you created in the previous step with the **--set controller.service.loadBalancerIP="" parameter**. + +```bash + helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx + helm repo update + helm upgrade --install --cleanup-on-fail --atomic ingress-nginx ingress-nginx/ingress-nginx \ + --namespace ingress-nginx \ + --create-namespace \ + --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-dns-label-name"=$MY_DNS_LABEL \ + --set controller.service.loadBalancerIP=$MY_STATIC_IP \ + --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz \ + --wait --timeout 10m0s +``` + +## Add HTTPS termination to custom domain + +At this point in the tutorial, you have an AKS web app with NGINX as the ingress controller and a custom domain you can use to access your application. The next step is to add an SSL certificate to the domain so that users can reach your application securely via https. + +### Set Up Cert Manager + +To add HTTPS, we're going to use Cert Manager. Cert Manager is an open source tool for obtaining and managing SSL certificates for Kubernetes deployments. Cert Manager obtains certificates from popular public issuers and private issuers, ensures the certificates are valid and up-to-date, and attempts to renew certificates at a configured time before they expire. +1. In order to install cert-manager, we must first create a namespace to run it in. This tutorial installs cert-manager into the cert-manager namespace. You can run cert-manager in a different namespace, but you must make modifications to the deployment manifests. +2. We can now install cert-manager. All resources are included in a single YAML manifest file. Install the manifest file with the following command: +3. Add the `certmanager.k8s.io/disable-validation: "true"` label to the cert-manager namespace by running the following. This allows the system resources that cert-manager requires to bootstrap TLS to be created in its own namespace. + +```bash +kubectl create namespace cert-manager +kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.7.0/cert-manager.crds.yaml +kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true +``` + +## Obtain certificate via Helm Charts + +Helm is a Kubernetes deployment tool for automating the creation, packaging, configuration, and deployment of applications and services to Kubernetes clusters. +Cert-manager provides Helm charts as a first-class method of installation on Kubernetes. +1. Add the Jetstack Helm repository. This repository is the only supported source of cert-manager charts. There are other mirrors and copies across the internet, but those are unofficial and could present a security risk. +2. Update local Helm Chart repository cache. +3. Install Cert-Manager addon via Helm. +4. Apply the certificate issuer YAML file. ClusterIssuers are Kubernetes resources that represent certificate authorities (CAs) that can generate signed certificates by honoring certificate signing requests. All cert-manager certificates require a referenced issuer that is in a ready condition to attempt to honor the request. You can find the issuer we're in the `cluster-issuer-prod.yml file`. + +```bash +helm repo add jetstack https://charts.jetstack.io +helm repo update +helm upgrade --install --cleanup-on-fail --atomic \ + --namespace cert-manager \ + --version v1.7.0 \ + --wait --timeout 10m0s \ + cert-manager jetstack/cert-manager +export SSL_EMAIL_ADDRESS="$(az account show --query user.name --output tsv)" +cluster_issuer_variables=$( +```text +Release "wordpress" does not exist. Installing it now. +NAME: wordpress +LAST DEPLOYED: Tue Oct 24 16:19:35 2023 +NAMESPACE: wordpress +STATUS: deployed +REVISION: 1 +TEST SUITE: None +NOTES: +CHART NAME: wordpress +CHART VERSION: 18.0.8 +APP VERSION: 6.3.2 + +** Please be patient while the chart is being deployed ** + +Your WordPress site can be accessed through the following DNS name from within your cluster: + + wordpress.wordpress.svc.cluster.local (port 80) + +To access your WordPress site from outside the cluster follow the steps below: + +1. Get the WordPress URL and associate WordPress hostname to your cluster external IP: + + export CLUSTER_IP=$(minikube ip) # On Minikube. Use: `kubectl cluster-info` on others K8s clusters + echo "WordPress URL: https://mydnslabelxxx.eastus.cloudapp.azure.com/" + echo "$CLUSTER_IP mydnslabelxxx.eastus.cloudapp.azure.com" | sudo tee -a /etc/hosts + export CLUSTER_IP=$(minikube ip) # On Minikube. Use: `kubectl cluster-info` on others K8s clusters + echo "WordPress URL: https://mydnslabelxxx.eastus.cloudapp.azure.com/" + echo "$CLUSTER_IP mydnslabelxxx.eastus.cloudapp.azure.com" | sudo tee -a /etc/hosts + +2. Open a browser and access WordPress using the obtained URL. + +3. Login with the following credentials below to see your blog: + + echo Username: wpcliadmin + echo Password: $(kubectl get secret --namespace wordpress wordpress -o jsonpath="{.data.wordpress-password}" | base64 -d) +``` + +## Browse your AKS deployment secured via HTTPS + +Wait for the cluster to setup. It often takes 2-3 minutes for the SSL certificate to propagate and about 5 minutes to have all WordPress POD replicas ready and the site to be fully reachable via https. + +```bash +runtime="5 minute" +endtime=$(date -ud "$runtime" +%s) +while [[ $(date -u +%s) -le $endtime ]]; do + export DEPLOYMENT_REPLICAS=$(kubectl -n wordpress get deployment wordpress -o=jsonpath='{.status.availableReplicas}'); + echo Current number of replicas "$DEPLOYMENT_REPLICAS/3"; + if [ "$DEPLOYMENT_REPLICAS" = "3" ]; then + break; + else + sleep 10; + fi; +done +``` + +## Verify Site works +Check that WordPress content is delivered correctly using the following command: + +```bash +if curl -I -s -f https://$FQDN > /dev/null ; then + curl -L -s -f https://$FQDN 2> /dev/null | head -n 9 +else + exit 1 +fi; +``` + +Results: + +```HTML +{ + + + + + + +WordPress on AKS + + +} +``` + +## Visit Application +Visit the website through the following URL: + +```bash +echo "You can now visit your web server at https://$FQDN" +``` + +## Clean up the resources (optional) + +To avoid Azure charges, you should clean up unneeded resources. When you no longer need the cluster, use the [az group delete](/cli/azure/group#az-group-delete) command to remove the resource group, container service, and all related resources. + +> [!NOTE] +> When you delete the cluster, the Microsoft Entra service principal used by the AKS cluster is not removed. For steps on how to remove the service principal, see [AKS service principal considerations and deletion](../../aks/kubernetes-service-principal.md#other-considerations). If you used a managed identity, the identity is managed by the platform and does not require removal. + +## Next steps + +- Learn how to [access the Kubernetes web dashboard](../../aks/kubernetes-dashboard.md) for your AKS cluster +- Learn how to [scale your cluster](../../aks/tutorial-kubernetes-scale.md) +- Learn how to manage your [Azure Database for MySQL flexible server instance](./quickstart-create-server-cli.md) +- Learn how to [configure server parameters](./how-to-configure-server-parameters-cli.md) for your database server diff --git a/scenarios/azure-docs/articles/aks/learn/quick-kubernetes-deploy-cli.md b/scenarios/azure-docs/articles/aks/learn/quick-kubernetes-deploy-cli.md index c0957f07c..3bb386a62 100644 --- a/scenarios/azure-docs/articles/aks/learn/quick-kubernetes-deploy-cli.md +++ b/scenarios/azure-docs/articles/aks/learn/quick-kubernetes-deploy-cli.md @@ -33,18 +33,6 @@ This quickstart assumes a basic understanding of Kubernetes concepts. For more i - Make sure that the identity you're using to create your cluster has the appropriate minimum permissions. For more details on access and identity for AKS, see [Access and identity options for Azure Kubernetes Service (AKS)](../concepts-identity.md). - If you have multiple Azure subscriptions, select the appropriate subscription ID in which the resources should be billed using the [az account set](/cli/azure/account#az-account-set) command. For more information, see [How to manage Azure subscriptions – Azure CLI](/cli/azure/manage-azure-subscriptions-azure-cli?tabs=bash#change-the-active-subscription). -## Define environment variables - -Define the following environment variables for use throughout this quickstart: - -```azurecli-interactive -export RANDOM_ID="$(openssl rand -hex 3)" -export MY_RESOURCE_GROUP_NAME="myAKSResourceGroup$RANDOM_ID" -export REGION="westeurope" -export MY_AKS_CLUSTER_NAME="myAKSCluster$RANDOM_ID" -export MY_DNS_LABEL="mydnslabel$RANDOM_ID" -``` - ## Create a resource group An [Azure resource group][azure-resource-group] is a logical group in which Azure resources are deployed and managed. When you create a resource group, you're prompted to specify a location. This location is the storage location of your resource group metadata and where your resources run in Azure if you don't specify another region during resource creation. @@ -52,6 +40,9 @@ An [Azure resource group][azure-resource-group] is a logical group in which Azur Create a resource group using the [`az group create`][az-group-create] command. ```azurecli-interactive +export RANDOM_ID="$(openssl rand -hex 3)" +export MY_RESOURCE_GROUP_NAME="myAKSResourceGroup$RANDOM_ID" +export REGION="westeurope" az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION ``` @@ -76,6 +67,7 @@ Results: Create an AKS cluster using the [`az aks create`][az-aks-create] command. The following example creates a cluster with one node and enables a system-assigned managed identity. ```azurecli-interactive +export MY_AKS_CLUSTER_NAME="myAKSCluster$RANDOM_ID" az aks create \ --resource-group $MY_RESOURCE_GROUP_NAME \ --name $MY_AKS_CLUSTER_NAME \ @@ -84,25 +76,18 @@ az aks create \ ``` > [!NOTE] -> When you create a new cluster, AKS automatically creates a second resource group to store the AKS resources. For more information, see [Why are two resource groups created with AKS?](../faq.md#why-are-two-resource-groups-created-with-aks) - -## Download credentials - -Configure `kubectl` to connect to your Kubernetes cluster using the [az aks get-credentials][az-aks-get-credentials] command. This command downloads credentials and configures the Kubernetes CLI to use them. - -```azurecli-interactive -az aks get-credentials --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_AKS_CLUSTER_NAME -``` +> When you create a new cluster, AKS automatically creates a second resource group to store the AKS resources. For more information, see [Why are two resource groups created with AKS?](../faq.yml) ## Connect to the cluster To manage a Kubernetes cluster, use the Kubernetes command-line client, [kubectl][kubectl]. `kubectl` is already installed if you use Azure Cloud Shell. To install `kubectl` locally, use the [`az aks install-cli`][az-aks-install-cli] command. -Verify the connection to your cluster using the [kubectl get][kubectl-get] command. This command returns a list of the cluster nodes. +1. Configure `kubectl` to connect to your Kubernetes cluster using the [az aks get-credentials][az-aks-get-credentials] command. This command downloads credentials and configures the Kubernetes CLI to use them. Then verify the connection to your cluster using the [kubectl get][kubectl-get] command. This command returns a list of the cluster nodes. -```azurecli-interactive -kubectl get nodes -``` + ```azurecli-interactive + az aks get-credentials --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_AKS_CLUSTER_NAME + kubectl get nodes + ``` ## Deploy the application @@ -359,30 +344,6 @@ To deploy the application, you use a manifest file to create all the objects req kubectl apply -f aks-store-quickstart.yaml ``` -## Test the application - -You can validate that the application is running by visiting the public IP address or the application URL. - -Get the application URL using the following commands: - -```azurecli-interactive -runtime="5 minutes" -endtime=$(date -ud "$runtime" +%s) -while [[ $(date -u +%s) -le $endtime ]] -do - STATUS=$(kubectl get pods -l app=store-front -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') - echo $STATUS - if [ "$STATUS" == 'True' ] - then - export IP_ADDRESS=$(kubectl get service store-front --output 'jsonpath={..status.loadBalancer.ingress[0].ip}') - echo "Service IP Address: $IP_ADDRESS" - break - else - sleep 10 - fi -done -``` - Results: ```HTML @@ -434,7 +395,7 @@ To learn more about AKS and walk through a complete code-to-deployment example, [kubernetes-concepts]: ../concepts-clusters-workloads.md [aks-tutorial]: ../tutorial-kubernetes-prepare-app.md -[azure-resource-group]: ../../azure-resource-manager/management/overview.md +[azure-resource-group]: /azure/azure-resource-manager/management/overview [az-aks-create]: /cli/azure/aks#az-aks-create [az-aks-get-credentials]: /cli/azure/aks#az-aks-get-credentials [az-aks-install-cli]: /cli/azure/aks#az-aks-install-cli @@ -442,4 +403,4 @@ To learn more about AKS and walk through a complete code-to-deployment example, [az-group-delete]: /cli/azure/group#az-group-delete [kubernetes-deployment]: ../concepts-clusters-workloads.md#deployments-and-yaml-manifests [aks-solution-guidance]: /azure/architecture/reference-architectures/containers/aks-start-here?toc=/azure/aks/toc.json&bc=/azure/aks/breadcrumb/toc.json -[baseline-reference-architecture]: /azure/architecture/reference-architectures/containers/aks/baseline-aks?toc=/azure/aks/toc.json&bc=/azure/aks/breadcrumb/toc.json +[baseline-reference-architecture]: /azure/architecture/reference-architectures/containers/aks/baseline-aks?toc=/azure/aks/toc.json&bc=/azure/aks/breadcrumb/toc.json \ No newline at end of file diff --git a/scenarios/azure-docs/articles/confidential-computing/confidential-enclave-nodes-aks-get-started.md b/scenarios/azure-docs/articles/confidential-computing/confidential-enclave-nodes-aks-get-started.md new file mode 100644 index 000000000..fefa977d9 --- /dev/null +++ b/scenarios/azure-docs/articles/confidential-computing/confidential-enclave-nodes-aks-get-started.md @@ -0,0 +1,249 @@ +--- +title: 'Quickstart: Deploy an AKS cluster with confidential computing Intel SGX agent nodes by using the Azure CLI' +description: Learn how to create an Azure Kubernetes Service (AKS) cluster with enclave confidential containers a Hello World app by using the Azure CLI. +author: angarg05 +ms.service: azure-virtual-machines +ms.subservice: azure-confidential-computing +ms.topic: quickstart +ms.date: 11/06/2023 +ms.author: ananyagarg +ms.custom: devx-track-azurecli, mode-api, innovation-engine +--- + +# Quickstart: Deploy an AKS cluster with confidential computing Intel SGX agent nodes by using the Azure CLI + +In this quickstart, you'll use the Azure CLI to deploy an Azure Kubernetes Service (AKS) cluster with enclave-aware (DCsv2/DCSv3) VM nodes. You'll then run a simple Hello World application in an enclave. + +AKS is a managed Kubernetes service that enables developers or cluster operators to quickly deploy and manage clusters. To learn more, read the AKS introduction and the overview of AKS confidential nodes. + +Features of confidential computing nodes include: + +- Linux worker nodes supporting Linux containers. +- Generation 2 virtual machine (VM) with Ubuntu 18.04 VM nodes. +- Intel SGX capable CPU to help run your containers in confidentiality protected enclave leveraging Encrypted Page Cache (EPC) memory. For more information, see Frequently asked questions for Azure confidential computing. +- Intel SGX DCAP Driver preinstalled on the confidential computing nodes. For more information, see Frequently asked questions for Azure confidential computing. + +> [!NOTE] +> DCsv2/DCsv3 VMs use specialized hardware that's subject to region availability. For more information, see the available SKUs and supported regions. + +## Prerequisites + +This quickstart requires: + +- A minimum of eight DCsv2/DCSv3/DCdsv3 cores available in your subscription. + + By default, there is no pre-assigned quota for Intel SGX VM sizes for your Azure subscriptions. You should follow these instructions to request for VM core quota for your subscriptions. + +## Create an AKS cluster with enclave-aware confidential computing nodes and Intel SGX add-on + +Use the following instructions to create an AKS cluster with the Intel SGX add-on enabled, add a node pool to the cluster, and verify what you created with a Hello World enclave application. + +### Create an AKS cluster with a system node pool and AKS Intel SGX Addon + +> [!NOTE] +> If you already have an AKS cluster that meets the prerequisite criteria listed earlier, skip to the next section to add a confidential computing node pool. + +Intel SGX AKS Addon "confcom" exposes the Intel SGX device drivers to your containers to avoid added changes to your pod YAML. + +## Create Resource Group + +First, create a resource group for the cluster by using the `az group create` command. + +```bash +export RANDOM_SUFFIX="$(openssl rand -hex 3)" +export RESOURCE_GROUP="myResourceGroup$RANDOM_SUFFIX" +export REGION="eastus2" +az group create --name $RESOURCE_GROUP --location $REGION +``` + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroupxxxxxx", + "location": "eastus2", + "managedBy": null, + "name": "myResourceGroupxxxxxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Create Cluster with Confidential Computing Add-on +Now create an AKS cluster with the confidential computing add-on enabled. This command deploys a new AKS cluster with a system node pool of non-confidential computing nodes. Confidential computing Intel SGX nodes are not recommended for system node pools. + +```bash +export AKS_CLUSTER="myAKSCluster$RANDOM_SUFFIX" +az aks create -g $RESOURCE_GROUP --name $AKS_CLUSTER --generate-ssh-keys --enable-addons confcom +``` + +## Add a user node pool with confidential computing capabilities to the AKS cluster + +Run the following command to add a user node pool of `Standard_DC4s_v3` size with two nodes to the AKS cluster. After you run the command, a new node pool with DCsv3 should be visible with confidential computing add-on DaemonSets. + +```bash +az aks nodepool add --cluster-name $AKS_CLUSTER --name confcompool1 --resource-group $RESOURCE_GROUP --node-vm-size Standard_DC4s_v3 --node-count 2 +``` + +## Get Credentials + +Get the credentials for your AKS cluster. + +```bash +az aks get-credentials --resource-group $RESOURCE_GROUP --name $AKS_CLUSTER +``` + +## Verify the node pool and add-on + +Use the `kubectl get pods` command to verify that the nodes are created properly and the SGX-related DaemonSets are running on DCsv3 node pools: + +```bash +kubectl get pods --all-namespaces +``` + +Results: + + + +```text +NAMESPACE NAME READY STATUS RESTARTS AGE +kube-system sgx-device-plugin-xxxxx 1/1 Running 0 5m +``` + +## Enable the confidential computing AKS add-on on the existing cluster + +To enable the confidential computing add-on, use the `az aks enable-addons` command with the `confcom` add-on, specifying your existing AKS cluster name and resource group. + +```bash +az aks enable-addons --addons confcom --name $AKS_CLUSTER --resource-group $RESOURCE_GROUP +``` + +### Verify that DaemonSets are running on confidential node pools + +```bash +kubectl get nodes +``` + +Results: + + + +```text +NAME STATUS ROLES AGE VERSION +aks-confcompool1-xxxxx-vmss000000 Ready agent 5m v1.xx.x +``` + +## Deploy Hello World from an isolated enclave application + +Deploy a file named `hello-world-enclave.yaml`. This deployment assumes that you've deployed the *confcom* add-on. + +```bash +cat < hello-world-enclave.yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: oe-helloworld + namespace: default +spec: + template: + metadata: + labels: + app: oe-helloworld + spec: + containers: + - name: oe-helloworld + image: mcr.microsoft.com/acc/samples/oe-helloworld:latest + resources: + limits: + sgx.intel.com/epc: "10Mi" + requests: + sgx.intel.com/epc: "10Mi" + volumeMounts: + - name: var-run-aesmd + mountPath: /var/run/aesmd + restartPolicy: "Never" + volumes: + - name: var-run-aesmd + hostPath: + path: /var/run/aesmd + backoffLimit: 0 +EOF +kubectl apply -f hello-world-enclave.yaml +``` + +Results: + + + +```text +job.batch/oe-helloworld created +``` + +## Check Jobs + +You can confirm that the workload successfully created a Trusted Execution Environment (enclave) by running the following commands: + +```bash +kubectl get jobs -l app=oe-helloworld +``` + +Results: + + + +```text +NAME COMPLETIONS DURATION AGE +oe-helloworld 1/1 1s 23s +``` + +## Check Pods + +```bash +kubectl get pods -l app=oe-helloworld +``` + +Results: + + + +```text +NAME READY STATUS RESTARTS AGE +oe-helloworld-xxxxx 0/1 Completed 0 25s +``` + +## Wait for Pod to finish deploying. + +```bash +while [[ $(kubectl get pods -l app=oe-helloworld -o 'jsonpath={..status.phase}') != "Succeeded" ]]; do + sleep 2 +done + +kubectl logs -l app=oe-helloworld +``` + +Results: + + + +```text +Hello world from the enclave +Enclave called into host to print: Hello World! +``` + +## Next steps + +- Run Python, Node, or other applications through confidential containers using ISV/OSS SGX wrapper software. Review [confidential container samples in GitHub](https://github.com/Azure-Samples/confidential-container-samples). + +- Run enclave-aware applications by using the [enclave-aware Azure container samples in GitHub](https://github.com/Azure-Samples/confidential-computing/blob/main/containersamples/). + + +[az-group-create]: /cli/azure/group#az_group_create + +[az-aks-create]: /cli/azure/aks#az_aks_create + +[az-aks-get-credentials]: /cli/azure/aks#az_aks_get_credentials \ No newline at end of file diff --git a/scenarios/azure-docs/articles/confidential-computing/hellow-world-enclave.yaml b/scenarios/azure-docs/articles/confidential-computing/hellow-world-enclave.yaml new file mode 100644 index 000000000..c877c63c6 --- /dev/null +++ b/scenarios/azure-docs/articles/confidential-computing/hellow-world-enclave.yaml @@ -0,0 +1,28 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: oe-helloworld + namespace: default +spec: + template: + metadata: + labels: + app: oe-helloworld + spec: + containers: + - name: oe-helloworld + image: mcr.microsoft.com/acc/samples/oe-helloworld:latest + resources: + limits: + sgx.intel.com/epc: "10Mi" + requests: + sgx.intel.com/epc: "10Mi" + volumeMounts: + - name: var-run-aesmd + mountPath: /var/run/aesmd + restartPolicy: "Never" + volumes: + - name: var-run-aesmd + hostPath: + path: /var/run/aesmd + backoffLimit: 0 \ No newline at end of file diff --git a/scenarios/azure-docs/articles/static-web-apps/get-started-cli.md b/scenarios/azure-docs/articles/static-web-apps/get-started-cli.md index 76bf1532a..b04a12b86 100644 --- a/scenarios/azure-docs/articles/static-web-apps/get-started-cli.md +++ b/scenarios/azure-docs/articles/static-web-apps/get-started-cli.md @@ -27,17 +27,6 @@ In this quickstart, you deploy a web application to Azure Static Web apps using - [Azure CLI](/cli/azure/install-azure-cli) installed (version 2.29.0 or higher). - [A Git setup](https://www.git-scm.com/downloads). -## Define environment variables - -The first step in this quickstart is to define environment variables. - -```bash -export RANDOM_ID="$(openssl rand -hex 3)" -export MY_RESOURCE_GROUP_NAME="myStaticWebAppResourceGroup$RANDOM_ID" -export REGION=EastUS2 -export MY_STATIC_WEB_APP_NAME="myStaticWebApp$RANDOM_ID" -``` - ## Create a repository (optional) (Optional) This article uses a GitHub template repository as another way to make it easy for you to get started. The template features a starter app to deploy to Azure Static Web Apps. @@ -55,6 +44,9 @@ export MY_STATIC_WEB_APP_NAME="myStaticWebApp$RANDOM_ID" Create a resource group. ```bash +export RANDOM_ID="$(openssl rand -hex 3)" +export MY_RESOURCE_GROUP_NAME="myStaticWebAppResourceGroup$RANDOM_ID" +export REGION=EastUS2 az group create \ --name $MY_RESOURCE_GROUP_NAME \ --location $REGION @@ -81,6 +73,7 @@ Results: Deploy the app as a static web app from the Azure CLI. ```bash +export MY_STATIC_WEB_APP_NAME="myStaticWebApp$RANDOM_ID" az staticwebapp create \ --name $MY_STATIC_WEB_APP_NAME \ --resource-group $MY_RESOURCE_GROUP_NAME \ diff --git a/scenarios/azure-docs/articles/virtual-machine-scale-sets/flexible-virtual-machine-scale-sets-cli.md b/scenarios/azure-docs/articles/virtual-machine-scale-sets/flexible-virtual-machine-scale-sets-cli.md index ad3c2fdb4..3db8ac0d1 100644 --- a/scenarios/azure-docs/articles/virtual-machine-scale-sets/flexible-virtual-machine-scale-sets-cli.md +++ b/scenarios/azure-docs/articles/virtual-machine-scale-sets/flexible-virtual-machine-scale-sets-cli.md @@ -25,33 +25,14 @@ The Azure Cloud Shell is a free interactive shell that you can use to run the st To open the Cloud Shell, select **Open Cloud Shell** from the upper right corner of a code block. You can also launch Cloud Shell in a separate browser tab by going to [https://shell.azure.com/cli](https://shell.azure.com/cli). Select **Copy** to copy the blocks of code, paste it into the Cloud Shell, and press enter to run it. -## Define environment variables +## Create a resource group -Define environment variables as follows. +A resource group is a logical container into which Azure resources are deployed and managed. All resources must be placed in a resource group. The following command creates a resource group with the previously defined $MY_RESOURCE_GROUP_NAME and $REGION parameters. ```bash export RANDOM_ID="$(openssl rand -hex 3)" export MY_RESOURCE_GROUP_NAME="myVMSSResourceGroup$RANDOM_ID" export REGION=EastUS -export MY_VMSS_NAME="myVMSS$RANDOM_ID" -export MY_USERNAME=azureuser -export MY_VM_IMAGE="Ubuntu2204" -export MY_VNET_NAME="myVNet$RANDOM_ID" -export NETWORK_PREFIX="$(($RANDOM % 254 + 1))" -export MY_VNET_PREFIX="10.$NETWORK_PREFIX.0.0/16" -export MY_VM_SN_NAME="myVMSN$RANDOM_ID" -export MY_VM_SN_PREFIX="10.$NETWORK_PREFIX.0.0/24" -export MY_APPGW_SN_NAME="myAPPGWSN$RANDOM_ID" -export MY_APPGW_SN_PREFIX="10.$NETWORK_PREFIX.1.0/24" -export MY_APPGW_NAME="myAPPGW$RANDOM_ID" -export MY_APPGW_PUBLIC_IP_NAME="myAPPGWPublicIP$RANDOM_ID" -``` - -## Create a resource group - -A resource group is a logical container into which Azure resources are deployed and managed. All resources must be placed in a resource group. The following command creates a resource group with the previously defined $MY_RESOURCE_GROUP_NAME and $REGION parameters. - -```bash az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION -o JSON ``` @@ -78,6 +59,11 @@ Now you'll create network resources. In this step you're going to create a virtu #### Create virtual network and subnet ```bash +export MY_VNET_NAME="myVNet$RANDOM_ID" +export NETWORK_PREFIX="$(($RANDOM % 254 + 1))" +export MY_VNET_PREFIX="10.$NETWORK_PREFIX.0.0/16" +export MY_VM_SN_NAME="myVMSN$RANDOM_ID" +export MY_VM_SN_PREFIX="10.$NETWORK_PREFIX.0.0/24" az network vnet create --name $MY_VNET_NAME --resource-group $MY_RESOURCE_GROUP_NAME --location $REGION --address-prefix $MY_VNET_PREFIX --subnet-name $MY_VM_SN_NAME --subnet-prefix $MY_VM_SN_PREFIX -o JSON ``` @@ -124,6 +110,10 @@ Results: Azure Application Gateway requires a dedicated subnet within your virtual network. The following command creates a subnet named $MY_APPGW_SN_NAME with a specified address prefix named $MY_APPGW_SN_PREFIX in your virtual network $MY_VNET_NAME. ```bash +export MY_APPGW_SN_NAME="myAPPGWSN$RANDOM_ID" +export MY_APPGW_SN_PREFIX="10.$NETWORK_PREFIX.1.0/24" +export MY_APPGW_NAME="myAPPGW$RANDOM_ID" +export MY_APPGW_PUBLIC_IP_NAME="myAPPGWPublicIP$RANDOM_ID" az network vnet subnet create --name $MY_APPGW_SN_NAME --resource-group $MY_RESOURCE_GROUP_NAME --vnet-name $MY_VNET_NAME --address-prefix $MY_APPGW_SN_PREFIX -o JSON ``` @@ -393,6 +383,9 @@ https://techcommunity.microsoft.com/t5/azure-compute-blog/breaking-change-for-vm Now create a Virtual Machine Scale Set with [az vmss create](/cli/azure/vmss). The following example creates a zone redundant scale set with an instance count of *2* with public IP in subnet $MY_VM_SN_NAME within your resource group $MY_RESOURCE_GROUP_NAME, integrates the Application Gateway, and generates SSH keys. Make sure to save the SSH keys if you need to log into your VMs via ssh. ```bash +export MY_VMSS_NAME="myVMSS$RANDOM_ID" +export MY_USERNAME=azureuser +export MY_VM_IMAGE="Ubuntu2204" az vmss create --name $MY_VMSS_NAME --resource-group $MY_RESOURCE_GROUP_NAME --image $MY_VM_IMAGE --admin-username $MY_USERNAME --generate-ssh-keys --public-ip-per-vm --orchestration-mode Uniform --instance-count 2 --zones 1 2 3 --vnet-name $MY_VNET_NAME --subnet $MY_VM_SN_NAME --vm-sku Standard_DS2_v2 --upgrade-policy-mode Automatic --app-gateway $MY_APPGW_NAME --backend-pool-name appGatewayBackendPool -o JSON ``` diff --git a/scenarios/azure-docs/articles/virtual-machine-scale-sets/tutorial-use-custom-image-cli.md b/scenarios/azure-docs/articles/virtual-machine-scale-sets/tutorial-use-custom-image-cli.md new file mode 100644 index 000000000..a7129cff1 --- /dev/null +++ b/scenarios/azure-docs/articles/virtual-machine-scale-sets/tutorial-use-custom-image-cli.md @@ -0,0 +1,210 @@ +--- +title: Tutorial - Use a custom VM image in a scale set with Azure CLI +description: Learn how to use the Azure CLI to create a custom VM image that you can use to deploy a Virtual Machine Scale Set +author: ju-shim +ms.service: azure-virtual-machine-scale-sets +ms.subservice: shared-image-gallery +ms.topic: tutorial +ms.date: 10/28/2024 +ms.reviewer: mimckitt +ms.author: jushiman +ms.custom: mvc, devx-track-azurecli, innovation-engine +--- + +# Tutorial: Create and use a custom image for Virtual Machine Scale Sets with the Azure CLI +When you create a scale set, you specify an image to be used when the VM instances are deployed. To reduce the number of tasks after VM instances are deployed, you can use a custom VM image. This custom VM image includes any required application installs or configurations. Any VM instances created in the scale set use the custom VM image and are ready to serve your application traffic. In this tutorial you learn how to: + +> [!div class="checklist"] +> * Create an Azure Compute Gallery +> * Create a specialized image definition +> * Create an image version +> * Create a scale set from a specialized image +> * Share an image gallery + +[!INCLUDE [quickstarts-free-trial-note](~/reusable-content/ce-skilling/azure/includes/quickstarts-free-trial-note.md)] + +[!INCLUDE [azure-cli-prepare-your-environment.md](~/reusable-content/azure-cli/azure-cli-prepare-your-environment.md)] + +- This article requires version 2.4.0 or later of the Azure CLI. If using Azure Cloud Shell, the latest version is already installed. + +## Overview +An [Azure Compute Gallery](../virtual-machines/shared-image-galleries.md) simplifies custom image sharing across your organization. Custom images are like marketplace images, but you create them yourself. Custom images can be used to bootstrap configurations such as preloading applications, application configurations, and other OS configurations. + +The Azure Compute Gallery lets you share your custom VM images with others. Choose which images you want to share, which regions you want to make them available in, and who you want to share them with. + +## Create and configure a source VM +First, create a resource group with [az group create](/cli/azure/group), then create a VM with [az vm create](/cli/azure/vm#az-vm-create). This VM is then used as the source for the image. + +The following example creates a Linux-based VM named *myVM* in the resource group named *myResourceGroup*. + +```azurecli-interactive +export RANDOM_ID=$(openssl rand -hex 3) +export MY_RESOURCE_GROUP_NAME="myResourceGroup$RANDOM_ID" +export REGION="eastus" +export MY_VM_NAME="myVM$RANDOM_ID" + +az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION + +az vm create \ + --resource-group $MY_RESOURCE_GROUP_NAME \ + --name $MY_VM_NAME \ + --image debian11 \ + --admin-username azureuser \ + --generate-ssh-keys +``` + +> [!TIP] +> The **ID** of your VM is shown in the output of the [az vm create](/cli/azure/vm#az-vm-create) command. Copy and store this in a safe location so you can use it later in this tutorial. + +## Create an image gallery +An image gallery is the primary resource used for enabling image sharing. + +Allowed characters for gallery names are uppercase or lowercase letters, digits, dots, and periods. The gallery name can't contain dashes. Gallery names must be unique within your subscription. + +Create an image gallery using [az sig create](/cli/azure/sig#az-sig-create). + +In the following example: + +* You create a resource group for the gallery named *myGalleryRG* located in *East US*. +* The gallery is named *myGallery*. + +```azurecli-interactive +export MY_GALLERY_RG_NAME="myGalleryRG$RANDOM_ID" +export MY_GALLERY_NAME="myGallery$RANDOM_ID" + +az group create --name $MY_GALLERY_RG_NAME --location $REGION +az sig create --resource-group $MY_GALLERY_RG_NAME --gallery-name $MY_GALLERY_NAME +``` + +## Create an image definition +Image definitions create a logical grouping for images. They're used to manage information about the image versions that are created within them. + +Image definition names can be made up of uppercase or lowercase letters, digits, dots, dashes, and periods. + +Make sure your image definition is the right type: + +* **State** - If you have generalized the VM (using Sysprep for Windows, or waagent -deprovision for Linux), then you should create a generalized image definition using `--os-state generalized`. If you want to use the VM without removing existing user accounts, create a specialized image definition using `--os-state specialized`. +* **Security type** - New Azure VMs are created with Trusted Launch configured by default. This tutorial includes subsequent code samples that reflect the Trusted Launch configuration when creating the image definition and scale set. If you're creating an image with a VM that doesn't have Trusted Launch enabled, make sure to reflect the correct security type when you create both of those resources. For more information about Trusted Launch, see [Trusted Launch for Azure virtual machines](/azure/virtual-machines/trusted-launch). + +For more information about the values you can specify for an image definition, see [Image definitions](../virtual-machines/shared-image-galleries.md#image-definitions). + +Create an image definition in the gallery using [az sig image-definition create](/cli/azure/sig/image-definition#az-sig-image-definition-create). + +In the following example, the image definition is: +* Named *myImageDefinition*. +* Configured for a [specialized](../virtual-machines/shared-image-galleries.md#generalized-and-specialized-images) Linux OS image. To create a definition for images using a Windows OS, use `--os-type Windows`. +* Configured for Trusted Launch. + +```azurecli-interactive +export MY_IMAGE_DEF_NAME="myImageDefinition$RANDOM_ID" +MY_PUBLISHER_NAME="myPublisher$RANDOM_ID" + +az sig image-definition create \ + --resource-group $MY_GALLERY_RG_NAME \ + --gallery-name $MY_GALLERY_NAME \ + --gallery-image-definition $MY_IMAGE_DEF_NAME \ + --publisher $MY_PUBLISHER_NAME \ + --offer myOffer \ + --sku mySKU \ + --os-type Linux \ + --os-state specialized \ + --features SecurityType=TrustedLaunch +``` + +> [!TIP] +> The **ID** of your image definition is shown in the output of the command. Copy and store this in a safe location so you can use it later in this tutorial. + +## Create the image version +Create an image version from the VM using [az image gallery create-image-version](/cli/azure/sig/image-version#az-sig-image-version-create). + +Allowed characters for the image version are numbers and periods. Numbers must be within the range of a 32-bit integer. Format: *MajorVersion*.*MinorVersion*.*Patch*. + +In the following example: + +* The version of the image is *1.0.0*. +* We create one replica in the *South Central US* region and one replica in the *East US* region. The replication regions must include the region the source VM is located. +* `--virtual-machine` is the ID of the VM we created previously. + +```azurecli-interactive +export MY_VM_ID=$(az vm show --name $MY_VM_NAME --resource-group $MY_RESOURCE_GROUP_NAME --query "id" --output tsv) + +az sig image-version create \ + --resource-group $MY_GALLERY_RG_NAME \ + --gallery-name $MY_GALLERY_NAME \ + --gallery-image-definition $MY_IMAGE_DEF_NAME \ + --gallery-image-version 1.0.0 \ + --virtual-machine $MY_VM_ID +``` + +> [!NOTE] +> You need to wait for the image version to completely finish being built and replicated before you can use the same image to create another image version. +> +> You can also store your image in Premium storage by a adding `--storage-account-type premium_lrs`, or [Zone Redundant Storage](/azure/storage/common/storage-redundancy) by adding `--storage-account-type standard_zrs` when you create the image version. + + +## Create a scale set from the image + +You create a scale set using [`az vmss create`](/cli/azure/vmss#az-vmss-create). If you're using a specialized source VM, add the `--specialized` parameter to indicate it's a specialized image. + +When you use the image definition ID for `--image` to create the scale set instances, you create a scale set that uses the latest version of the image that is available. If you want a specific version of the image, make sure you include the image _version_ ID when you define the `--image`. + +* **Latest image example**: `/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Compute/galleries/myGallery/images/myImage` + +* **Specific image example**: `/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Compute/galleries/myGallery/images/myImage/versions/1.0.0` + +In the following example, the scale set is: +* Named *myScaleSet* +* Using the latest version of the *myImageDefinition* image. +* Configured for Trusted Launch. + +```azurecli +export MY_IMAGE_DEF_ID=$(az sig image-definition show --resource-group $MY_GALLERY_RG_NAME --gallery-name $MY_GALLERY_NAME --gallery-image-definition $MY_IMAGE_DEF_NAME --query "id" --output tsv) +export MY_SCALE_SET_RG_NAME="myResourceGroup$RANDOM_ID" +export MY_SCALE_SET_NAME="myScaleSet$RANDOM_ID" + +az group create --name $MY_SCALE_SET_RG_NAME --location $REGION + +az vmss create \ + --resource-group $MY_SCALE_SET_RG_NAME \ + --name $MY_SCALE_SET_NAME \ + --orchestration-mode flexible \ + --image $MY_IMAGE_DEF_ID \ + --specialized \ + --security-type TrustedLaunch +``` + +It takes a few minutes to create and configure all the scale set resources and VMs. + +## Share the gallery + +You can share images across subscriptions using Azure role-based access control (Azure RBAC), and you can share them at the gallery, image definition, or image version levels. Any user with read permission to an image version, even across subscriptions, is able to deploy a VM using the image version. + +We recommend that you share with other users at the gallery level. + +The following example: +* Gets the object ID of the gallery using [az sig show](/cli/azure/sig#az-sig-show). +* Provides access to the gallery using [az role assignment create](/cli/azure/role/assignment#az-role-assignment-create). + * Uses the object ID as the scope of the assignment. + * Uses the signed-in user's ID as the assignee for demonstration purposes. When you use this code in your test or production code, make sure you update the assignee to reflect who you want to be able to access this image. For more information about how to share resources using Azure RBAC, see [Add or remove Azure role assignments using Azure CLI](/azure/role-based-access-control/role-assignments-cli). , along with an email address, using [az role assignment create](/cli/azure/role/assignment#az-role-assignment-create) to give a user access to the shared image gallery. + +For example, you can get the gallery ID and assign the Reader role to the signed-in user. This allows the user to access the shared image gallery. + +Note: Ensure you have the necessary permissions to perform these operations and that the target user or service principal has the appropriate access to the shared resources. + +## Clean up resources +To remove your scale set and additional resources, delete the resource group and all its resources with [az group delete](/cli/azure/group). The `--no-wait` parameter returns control to the prompt without waiting for the operation to complete. The `--yes` parameter confirms that you wish to delete the resources without an additional prompt to do so. + +## Next steps +In this tutorial, you learned how to create and use a custom VM image for your scale sets with the Azure CLI: + +> [!div class="checklist"] +> * Create an Azure Compute Gallery +> * Create a specialized image definition +> * Create an image version +> * Create a scale set from a specialized image +> * Share an image gallery + +Advance to the next tutorial to learn how to deploy applications to your scale set. + +> [!div class="nextstepaction"] +> [Deploy applications to your scale sets](tutorial-install-apps-cli.md) \ No newline at end of file diff --git a/scenarios/azure-docs/articles/virtual-machines/linux/attach-disk-portal.yml b/scenarios/azure-docs/articles/virtual-machines/linux/attach-disk-portal.yml deleted file mode 100644 index babdb3954..000000000 --- a/scenarios/azure-docs/articles/virtual-machines/linux/attach-disk-portal.yml +++ /dev/null @@ -1,259 +0,0 @@ -### YamlMime:HowTo - -metadata: - title: Attach a data disk to a Linux VM - description: Use the portal to attach new or existing data disk to a Linux VM. - author: roygara - ms.author: rogarana - ms.date: 03/19/2024 - ms.service: azure-disk-storage - ms.topic: how-to - ms.collection: linux - ms.custom: - - linux-related-content - - ge-structured-content-pilot - -title: | - Use the portal to attach a data disk to a Linux VM -introduction: | - **Applies to:** :heavy_check_mark: Linux VMs :heavy_check_mark: Flexible scale sets - - This article shows you how to attach both new and existing disks to a Linux virtual machine through the Azure portal. You can also [attach a data disk to a Windows VM in the Azure portal](../windows/attach-managed-disk-portal.yml). - -prerequisites: - summary: | - Before you attach disks to your VM, review these tips: - dependencies: - - The size of the virtual machine controls how many data disks you can attach. For details, see [Sizes for virtual machines](../sizes.md). - -procedureSection: - - title: | - Find the virtual machine - summary: | - Follow these steps: - steps: - - | - Go to the [Azure portal](https://portal.azure.com/) to find the VM. Search for and select **Virtual machines**. - - | - Select the VM you'd like to attach the disk to from the list. - - | - In the **Virtual machines** page, under **Settings**, select **Disks**. - - - title: | - Attach a new disk - summary: | - Follow these steps: - steps: - - | - On the **Disks** pane, under **Data disks**, select **Create and attach a new disk**. - - | - Enter a name for your managed disk. Review the default settings, and update the **Storage type**, **Size (GiB)**, **Encryption** and **Host caching** as necessary. - - :::image type="content" source="./media/attach-disk-portal/create-new-md.png" alt-text="Screenshot of review disk settings." lightbox="./media/attach-disk-portal/create-new-md.png"::: - - - | - When you're done, select **Save** at the top of the page to create the managed disk and update the VM configuration. - - - title: | - Attach an existing disk - summary: | - Follow these steps: - steps: - - | - On the **Disks** pane, under **Data disks**, select **Attach existing disks**. - - | - Select the drop-down menu for **Disk name** and select a disk from the list of available managed disks. - - | - Select **Save** to attach the existing managed disk and update the VM configuration: - - - title: | - Connect to the Linux VM to mount the new disk - summary: | - To partition, format, and mount your new disk so your Linux VM can use it, SSH into your VM. For more information, see [How to use SSH with Linux on Azure](mac-create-ssh-keys.md). The following example connects to a VM with the public IP address of *10.123.123.25* with the username *azureuser*: - code: | - ```bash - ssh azureuser@10.123.123.25 - ``` - - - title: | - Find the disk - summary: | - Once connected to your VM, you need to find the disk. In this example, we're using `lsblk` to list the disks. - code: | - ```bash - lsblk -o NAME,HCTL,SIZE,MOUNTPOINT | grep -i "sd" - ``` - - The output is similar to the following example: - - ```output - sda 0:0:0:0 30G - ├─sda1 29.9G / - ├─sda14 4M - └─sda15 106M /boot/efi - sdb 1:0:1:0 14G - └─sdb1 14G /mnt - sdc 3:0:0:0 4G - ``` - - In this example, the disk that was added was `sdc`. It's a LUN 0 and is 4GB. - - For a more complex example, here's what multiple data disks look like in the portal: - - :::image type="content" source="./media/attach-disk-portal/find-disk.png" alt-text="Screenshot of multiple disks shown in the portal."::: - - In the image, you can see that there are 3 data disks: 4 GB on LUN 0, 16GB at LUN 1, and 32G at LUN 2. - - Here's what that might look like using `lsblk`: - - ```output - sda 0:0:0:0 30G - ├─sda1 29.9G / - ├─sda14 4M - └─sda15 106M /boot/efi - sdb 1:0:1:0 14G - └─sdb1 14G /mnt - sdc 3:0:0:0 4G - sdd 3:0:0:1 16G - sde 3:0:0:2 32G - ``` - - From the output of `lsblk` you can see that the 4GB disk at LUN 0 is `sdc`, the 16GB disk at LUN 1 is `sdd`, and the 32G disk at LUN 2 is `sde`. - - ### Prepare a new empty disk - - > [!IMPORTANT] - > If you are using an existing disk that contains data, skip to [mounting the disk](#mount-the-disk). - > The following instructions will delete data on the disk. - - If you're attaching a new disk, you need to partition the disk. - - The `parted` utility can be used to partition and to format a data disk. - - Use the latest version `parted` that is available for your distro. - - If the disk size is 2 tebibytes (TiB) or larger, you must use GPT partitioning. If disk size is under 2 TiB, then you can use either MBR or GPT partitioning. - - - The following example uses `parted` on `/dev/sdc`, which is where the first data disk will typically be on most VMs. Replace `sdc` with the correct option for your disk. We're also formatting it using the [XFS](https://xfs.wiki.kernel.org/) filesystem. - - ```bash - sudo parted /dev/sdc --script mklabel gpt mkpart xfspart xfs 0% 100% - sudo mkfs.xfs /dev/sdc1 - sudo partprobe /dev/sdc1 - ``` - - Use the [`partprobe`](https://linux.die.net/man/8/partprobe) utility to make sure the kernel is aware of the new partition and filesystem. Failure to use `partprobe` can cause the blkid or lslbk commands to not return the UUID for the new filesystem immediately. - - ### Mount the disk - - Create a directory to mount the file system using `mkdir`. The following example creates a directory at `/datadrive`: - - ```bash - sudo mkdir /datadrive - ``` - - Use `mount` to then mount the filesystem. The following example mounts the */dev/sdc1* partition to the `/datadrive` mount point: - - ```bash - sudo mount /dev/sdc1 /datadrive - ``` - To ensure that the drive is remounted automatically after a reboot, it must be added to the */etc/fstab* file. It's also highly recommended that the UUID (Universally Unique Identifier) is used in */etc/fstab* to refer to the drive rather than just the device name (such as, */dev/sdc1*). If the OS detects a disk error during boot, using the UUID avoids the incorrect disk being mounted to a given location. Remaining data disks would then be assigned those same device IDs. To find the UUID of the new drive, use the `blkid` utility: - - ```bash - sudo blkid - ``` - - The output looks similar to the following example: - - ```output - /dev/sda1: LABEL="cloudimg-rootfs" UUID="11111111-1b1b-1c1c-1d1d-1e1e1e1e1e1e" TYPE="ext4" PARTUUID="1a1b1c1d-11aa-1234-1a1a1a1a1a1a" - /dev/sda15: LABEL="UEFI" UUID="BCD7-96A6" TYPE="vfat" PARTUUID="1e1g1cg1h-11aa-1234-1u1u1a1a1u1u" - /dev/sdb1: UUID="22222222-2b2b-2c2c-2d2d-2e2e2e2e2e2e" TYPE="ext4" TYPE="ext4" PARTUUID="1a2b3c4d-01" - /dev/sda14: PARTUUID="2e2g2cg2h-11aa-1234-1u1u1a1a1u1u" - /dev/sdc1: UUID="33333333-3b3b-3c3c-3d3d-3e3e3e3e3e3e" TYPE="xfs" PARTLABEL="xfspart" PARTUUID="c1c2c3c4-1234-cdef-asdf3456ghjk" - ``` - - > [!NOTE] - > Improperly editing the **/etc/fstab** file could result in an unbootable system. If unsure, refer to the distribution's documentation for information on how to properly edit this file. You should create a backup of the **/etc/fstab** file is created before editing. - - Next, open the **/etc/fstab** file in a text editor. Add a line to the end of the file, using the UUID value for the `/dev/sdc1` device that was created in the previous steps, and the mountpoint of `/datadrive`. Using the example from this article, the new line would look like the following: - - ```config - UUID=33333333-3b3b-3c3c-3d3d-3e3e3e3e3e3e /datadrive xfs defaults,nofail 1 2 - ``` - - When you're done editing the file, save and close the editor. - - > [!NOTE] - > Later removing a data disk without editing fstab could cause the VM to fail to boot. Most distributions provide either the *nofail* and/or *nobootwait* fstab options. These options allow a system to boot even if the disk fails to mount at boot time. Consult your distribution's documentation for more information on these parameters. - > - > The *nofail* option ensures that the VM starts even if the filesystem is corrupt or the disk does not exist at boot time. Without this option, you may encounter behavior as described in [Cannot SSH to Linux VM due to FSTAB errors](/archive/blogs/linuxonazure/cannot-ssh-to-linux-vm-after-adding-data-disk-to-etcfstab-and-rebooting) - - - - title: | - Verify the disk - summary: | - You can now use `lsblk` again to see the disk and the mountpoint. - - ```bash - lsblk -o NAME,HCTL,SIZE,MOUNTPOINT | grep -i "sd" - ``` - - The output will look something like this: - - ```output - sda 0:0:0:0 30G - ├─sda1 29.9G / - ├─sda14 4M - └─sda15 106M /boot/efi - sdb 1:0:1:0 14G - └─sdb1 14G /mnt - sdc 3:0:0:0 4G - └─sdc1 4G /datadrive - ``` - - You can see that `sdc` is now mounted at `/datadrive`. - - ### TRIM/UNMAP support for Linux in Azure - - Some Linux kernels support TRIM/UNMAP operations to discard unused blocks on the disk. This feature is primarily useful to inform Azure that deleted pages are no longer valid and can be discarded. This feature can save money on disks that are billed based on the amount of consumed storage, such as unmanaged standard disks and disk snapshots. - - There are two ways to enable TRIM support in your Linux VM. As usual, consult your distribution for the recommended approach: - steps: - - | - Use the `discard` mount option in */etc/fstab*, for example: - - ```config - UUID=33333333-3b3b-3c3c-3d3d-3e3e3e3e3e3e /datadrive xfs defaults,discard 1 2 - ``` - - | - In some cases, the `discard` option may have performance implications. Alternatively, you can run the `fstrim` command manually from the command line, or add it to your crontab to run regularly: - - **Ubuntu** - - ```bash - sudo apt-get install util-linux - sudo fstrim /datadrive - ``` - - **RHEL** - - ```bash - sudo yum install util-linux - sudo fstrim /datadrive - ``` - - **SUSE** - - ```bash - sudo zypper install util-linux - sudo fstrim /datadrive - ``` - -relatedContent: - - text: Troubleshoot Linux VM device name changes - url: /troubleshoot/azure/virtual-machines/troubleshoot-device-names-problems - - text: Attach a data disk using the Azure CLI - url: add-disk.md -#For more information, and to help troubleshoot disk issues, see [Troubleshoot Linux VM device name changes](/troubleshoot/azure/virtual-machines/troubleshoot-device-names-problems). - -#You can also [attach a data disk](add-disk.md) using the Azure CLI. diff --git a/scenarios/azure-docs/articles/virtual-machines/linux/disk-encryption-faq.yml b/scenarios/azure-docs/articles/virtual-machines/linux/disk-encryption-faq.yml deleted file mode 100644 index f77fa18bd..000000000 --- a/scenarios/azure-docs/articles/virtual-machines/linux/disk-encryption-faq.yml +++ /dev/null @@ -1,200 +0,0 @@ -### YamlMime:FAQ -metadata: - title: FAQ - Azure Disk Encryption for Linux VMs - description: This article provides answers to frequently asked questions about Microsoft Azure Disk Encryption for Linux IaaS VMs. - author: msmbaldwin - ms.service: azure-virtual-machines - ms.collection: linux - ms.subservice: security - ms.topic: faq - ms.author: mbaldwin - ms.date: 08/06/2024 -title: Azure Disk Encryption for Linux virtual machines FAQ -summary: | - This article provides answers to frequently asked questions (FAQ) about Azure Disk Encryption for Linux virtual machines (VMs). For more information about this service, see [Azure Disk Encryption overview](disk-encryption-overview.md). - - -sections: - - name: Ignored - questions: - - question: | - What is Azure Disk Encryption for Linux virtual machines? - answer: | - Azure Disk Encryption for Linux virtual machines uses the dm-crypt feature of Linux to provide full disk encryption of the OS disk* and data disks. Additionally, it provides encryption of the temporary disk when using the [EncryptFormatAll feature](disk-encryption-linux.md#use-encryptformatall-feature-for-data-disks-on-linux-vms). The content flows encrypted from the VM to the Storage backend with a customer-managed key. - - See [Supported virtual machines and operating systems](disk-encryption-overview.md#supported-vms-and-operating-systems). - - - question: | - Where is Azure Disk Encryption in general availability (GA)? - answer: | - Azure Disk Encryption for Linux virtual machines is in general availability in all Azure public regions. - - - question: | - What user experiences are available with Azure Disk Encryption? - answer: | - Azure Disk Encryption GA supports Azure Resource Manager templates, Azure PowerShell, and Azure CLI. The different user experiences give you flexibility. You have three different options for enabling disk encryption for your virtual machines. For more information on the user experience and step-by-step guidance available in Azure Disk Encryption, see [Azure Disk Encryption scenarios for Linux](disk-encryption-linux.md). - - - question: | - How much does Azure Disk Encryption cost? - answer: | - There's no charge for encrypting VM disks with Azure Disk Encryption, but there are charges associated with the use of Azure Key Vault. For more information on Azure Key Vault costs, see the [Key Vault pricing](https://azure.microsoft.com/pricing/details/key-vault/) page. - - - question: | - How can I start using Azure Disk Encryption? - answer: | - To get started, read the [Azure Disk Encryption overview](disk-encryption-overview.md). - - - question: | - What VM sizes and operating systems support Azure Disk Encryption? - answer: | - The [Azure Disk Encryption overview](disk-encryption-overview.md) article lists the [VM sizes](disk-encryption-overview.md#supported-vms) and [VM operating systems](disk-encryption-overview.md#supported-operating-systems) that support Azure Disk Encryption. - - - question: | - Can I encrypt both boot and data volumes with Azure Disk Encryption? - answer: | - Yes, you can encrypt both boot and data volumes, or you can encrypt the data volume without having to encrypt the OS volume first. - - After you've encrypted the OS volume, disabling encryption on the OS volume isn't supported. For Linux virtual machines in a scale set, only the data volume can be encrypted. - - - question: | - Can I encrypt an unmounted volume with Azure Disk Encryption? - answer: | - No, Azure Disk Encryption only encrypts mounted volumes. - - - question: | - What is Storage server-side encryption? - answer: | - Storage server-side encryption encrypts Azure managed disks in Azure Storage. Managed disks are encrypted by default with Server-side encryption with a platform-managed key (as of June 10, 2017). You can manage encryption of managed disks with your own keys by specifying a customer-managed key. For more information see: [Server-side encryption of Azure managed disks](../disk-encryption.md). - - - question: | - How is Azure Disk Encryption different from other disk encryption solutions and when should I use each solution? - answer: | - See [Overview of managed disk encryption options](../disk-encryption-overview.md). - - - question: | - How do I rotate secrets or encryption keys? - answer: | - To rotate secrets, just call the same command you used originally to enable disk encryption, specifying a different Key Vault. To rotate the key encryption key, call the same command you used originally to enable disk encryption, specifying the new key encryption. - - >[!WARNING] - > - If you previously used [Azure Disk Encryption with Microsoft Entra app](disk-encryption-linux-aad.md) by specifying Microsoft Entra credentials to encrypt this VM, you must continue to use this option to encrypt your VM. You can't use Azure Disk Encryption on this encrypted VM as this isn't a supported scenario, meaning switching away from Microsoft Entra application for this encrypted VM isn't supported yet. - - - question: | - How do I add or remove a key encryption key if I didn't originally use one? - answer: | - To add a key encryption key, call the enable command again passing the key encryption key parameter. To remove a key encryption key, call the enable command again without the key encryption key parameter. - - - question: | - Does Azure Disk Encryption allow you to bring your own key (BYOK)? - answer: | - Yes, you can supply your own key encryption keys. These keys are safeguarded in Azure Key Vault, which is the key store for Azure Disk Encryption. For more information on the key encryption keys support scenarios, see [Creating and configuring a key vault for Azure Disk Encryption](disk-encryption-key-vault.md). - - - question: | - Can I use an Azure-created key encryption key? - answer: | - Yes, you can use Azure Key Vault to generate a key encryption key for Azure disk encryption use. These keys are safeguarded in Azure Key Vault, which is the key store for Azure Disk Encryption. For more information on the key encryption key, see [Creating and configuring a key vault for Azure Disk Encryption](disk-encryption-key-vault.md). - - - question: | - Can I use an on-premises key management service or HSM to safeguard the encryption keys? - answer: | - You can't use the on-premises key management service or HSM to safeguard the encryption keys with Azure Disk Encryption. You can only use the Azure Key Vault service to safeguard the encryption keys. For more information on the key encryption key support scenarios, see [Creating and configuring a key vault for Azure Disk Encryption](disk-encryption-key-vault.md). - - - question: | - What are the prerequisites to configure Azure Disk Encryption? - answer: | - There are prerequisites for Azure Disk Encryption. See the [Creating and configuring a key vault for Azure Disk Encryption](disk-encryption-key-vault.md) article to create a new key vault, or set up an existing key vault for disk encryption access to enable encryption, and safeguard secrets and keys. For more information on the key encryption key support scenarios, see [Creating and configuring a key vault for Azure Disk Encryption](disk-encryption-key-vault.md). - - - question: | - What are the prerequisites to configure Azure Disk Encryption with a Microsoft Entra app (previous release)? - answer: | - There are prerequisites for Azure Disk Encryption. See the [Azure Disk Encryption with Microsoft Entra ID](disk-encryption-linux-aad.md) content to create an Microsoft Entra application, create a new key vault, or set up an existing key vault for disk encryption access to enable encryption, and safeguard secrets and keys. For more information on the key encryption key support scenarios, see [Creating and configuring a key vault for Azure Disk Encryption with Microsoft Entra ID](disk-encryption-key-vault-aad.md). - - - question: | - Is Azure Disk Encryption using a Microsoft Entra app (previous release) still supported? - answer: | - Yes. Disk encryption using a Microsoft Entra app is still supported. However, when encrypting new virtual machines it's recommended that you use the new method rather than encrypting with a Microsoft Entra app. - - - question: | - Can I migrate virtual machines that were encrypted with a Microsoft Entra app to encryption without a Microsoft Entra app? - answer: Currently, there isn't a direct migration path for machines that were encrypted with a Microsoft Entra app to encryption without a Microsoft Entra app. Additionally, there isn't a direct path from encryption without a Microsoft Entra app to encryption with an AD app. - - - question: | - What version of Azure PowerShell does Azure Disk Encryption support? - answer: | - Use the latest version of the Azure PowerShell SDK to configure Azure Disk Encryption. Download the latest version of [Azure PowerShell](https://github.com/Azure/azure-powershell/releases). Azure Disk Encryption is *not* supported by Azure SDK version 1.1.0. - - > [!NOTE] - > The Linux Azure disk encryption preview extension "Microsoft.OSTCExtension.AzureDiskEncryptionForLinux" is deprecated. This extension was published for Azure disk encryption preview release. You should not use the preview version of the extension in your testing or production deployment. - - > For deployment scenarios like Azure Resource Manager (ARM), where you have a need to deploy Azure disk encryption extension for Linux VM to enable encryption on your Linux IaaS VM, you must use the Azure disk encryption production supported extension "Microsoft.Azure.Security.AzureDiskEncryptionForLinux". - - - question: | - Can I apply Azure Disk Encryption on my custom Linux image? - answer: | - You can't apply Azure Disk Encryption on your custom Linux image. Only the gallery Linux images for the supported distributions called out previously are supported. Custom Linux images aren't currently supported. - - - question: | - Can I apply updates to a Linux Red Hat VM that uses the yum update? - answer: | - Yes, you can perform a yum update on a Red Hat Linux VM. For more information, see [Azure Disk Encryption on an isolated network](disk-encryption-isolated-network.md). - - - question: | - What is the recommended Azure disk encryption workflow for Linux? - answer: | - The following workflow is recommended to have the best results on Linux: - * Start from the unmodified stock gallery image corresponding to the needed OS distro and version - * Back up any mounted drives you want encrypted. This back up allows for recovery if there's a failure, for example if the VM is rebooted before encryption has completed. - * Encrypt (can take several hours or even days depending on VM characteristics and size of any attached data disks) - * Customize, and add software to the image as needed. - - If this workflow isn't possible, relying on [Storage Service Encryption (SSE)](../../storage/common/storage-service-encryption.md) at the platform storage account layer may be an alternative to full disk encryption using dm-crypt. - - - question: | - What is the disk "Bek Volume" or "/mnt/azure_bek_disk"? - answer: | - The "Bek volume" is a local data volume that securely stores the encryption keys for Encrypted Azure virtual machines. - > [!NOTE] - > Do not delete or edit any contents in this disk. Do not unmount the disk since the encryption key presence is needed for any encryption operations on the IaaS VM. - - - - question: | - What encryption method does Azure Disk Encryption use? - answer: | - Azure Disk Encryption uses the decrypt default of aes-xts-plain64 with a 256-bit volume master key. - - - question: | - If I use EncryptFormatAll and specify all volume types, will it erase the data on the data drives that we already encrypted? - answer: | - No, data won't be erased from data drives that are already encrypted using Azure Disk Encryption. Similar to how EncryptFormatAll didn't re-encrypt the OS drive, it won't re-encrypt the already encrypted data drive. For more information, see the [EncryptFormatAll criteria](disk-encryption-linux.md#use-encryptformatall-feature-for-data-disks-on-linux-vms). - - - question: | - Is XFS filesystem supported? - answer: | - Encryption of XFS OS disks is supported. - - Encryption of XFS data disks is supported only when the EncryptFormatAll parameter is used. This option reformats the volume, erasing any data previously there. For more information, see the [EncryptFormatAll criteria](disk-encryption-linux.md#use-encryptformatall-feature-for-data-disks-on-linux-vms). - - - question: | - Is resizing the OS partition supported? - answer: | - Resize of an Azure Disk Encryption encrypted OS disk isn't supported. - - - question: | - Can I backup and restore an encrypted VM? - answer: | - Azure Backup provides a mechanism to backup and restore encrypted VM's within the same subscription and region. For instructions, please see [Back up and restore encrypted virtual machines with Azure Backup](../../backup/backup-azure-vms-encryption.md). Restoring an encrypted VM to a different region is not currently supported. - - - question: | - Where can I go to ask questions or provide feedback? - answer: | - You can ask questions or provide feedback on the [Microsoft Q&A question page for Azure Disk Encryption](/answers/topics/azure-disk-encryption.html). - -additionalContent: | - - ## Next steps - - In this document, you learned more about the most frequent questions related to Azure Disk Encryption. For more information about this service, see the following articles: - - - [Azure Disk Encryption Overview](disk-encryption-overview.md) - - [Apply disk encryption in Azure Security Center](../../security-center/asset-inventory.md) - - [Azure data encryption at rest](../../security/fundamentals/encryption-atrest.md) diff --git a/scenarios/azure-docs/articles/virtual-machines/linux/faq.yml b/scenarios/azure-docs/articles/virtual-machines/linux/faq.yml deleted file mode 100644 index 5700bcc9c..000000000 --- a/scenarios/azure-docs/articles/virtual-machines/linux/faq.yml +++ /dev/null @@ -1,141 +0,0 @@ -### YamlMime:FAQ -metadata: - title: Frequently asked questions for Linux VMs in Azure - description: Provides answers to some of the common questions about Linux virtual machines created with the Resource Manager model. - author: ju-shim - ms.service: azure-virtual-machines - ms.collection: linux - ms.topic: faq - ms.date: 03/06/2024 - ms.author: jushiman -title: Frequently asked question about Linux Virtual Machines -summary: | - This article addresses some common questions about Linux virtual machines created in Azure using the Resource Manager deployment model. For the Windows version of this topic, see [Frequently asked question about Windows Virtual Machines](../windows/faq.yml) - - -sections: - - name: Ignored - questions: - - question: | - What can I run on an Azure VM? - answer: | - All subscribers can run server software on an Azure virtual machine. For more information, see [Linux on Azure-Endorsed Distributions](endorsed-distros.md) - - - question: | - How much storage can I use with a virtual machine? - answer: | - Each data disk can be up to 32,767 GiB. The number of data disks you can use depends on the size of the virtual machine. For details, see [Sizes for Virtual Machines](../sizes.md). - - Azure Managed Disks are the recommended disk storage offerings for use with Azure Virtual Machines for persistent storage of data. You can use multiple Managed Disks with each Virtual Machine. Managed Disks offer two types of durable storage options: Premium and Standard Managed Disks. For pricing information, see [Managed Disks Pricing](https://azure.microsoft.com/pricing/details/managed-disks). - - Azure storage accounts can also provide storage for the operating system disk and any data disks. Each disk is a .vhd file stored as a page blob. For pricing details, see [Storage Pricing Details](https://azure.microsoft.com/pricing/details/storage/). - - - question: | - How can I access my virtual machine? - answer: | - Establish a remote connection to sign on to the virtual machine, using Secure Shell (SSH). See the instructions on how to connect [from Windows](ssh-from-windows.md) or - [from Linux and Mac](mac-create-ssh-keys.md). By default, SSH allows a maximum of 10 concurrent connections. You can increase this number by editing the configuration file. - - If you’re having problems, check out [Troubleshoot Secure Shell (SSH) connections](/troubleshoot/azure/virtual-machines/troubleshoot-ssh-connection?toc=%2fazure%2fvirtual-machines%2flinux%2ftoc.json). - - - question: | - Can I use the temporary disk (/dev/sdb1) to store data? - answer: | - Don't use the temporary disk (/dev/sdb1) to store data. It is only there for temporary storage. You risk losing data that can’t be recovered. - - - question: | - Can I copy or clone an existing Azure VM? - answer: | - Yes. For instructions, see [How to create a copy of a Linux virtual machine in the Resource Manager deployment model](/previous-versions/azure/virtual-machines/linux/copy-vm). - - - question: | - Why am I not seeing Canada Central and Canada East regions through Azure Resource Manager? - answer: | - The two new regions of Canada Central and Canada East are not automatically registered for virtual machine creation for existing Azure subscriptions. This registration is done automatically when a virtual machine is deployed through the Azure portal to any other region using Azure Resource Manager. After a virtual machine is deployed to any other Azure region, the new regions should be available for subsequent virtual machines. - - - question: | - Can I add a NIC to my VM after it's created? - answer: | - Yes, this is now possible. The VM first needs to be stopped deallocated. Then you can add or remove a NIC (unless it's the last NIC on the VM). - - - question: | - Are there any computer name requirements? - answer: | - Yes. The computer name can be a maximum of 64 characters in length. See [Naming conventions rules and restrictions](/azure/architecture/best-practices/resource-naming) for more information around naming your resources. - - - question: | - Are there any resource group name requirements? - answer: | - Yes. The resource group name can be a maximum of 90 characters in length. See [Naming conventions rules and restrictions](/azure/architecture/best-practices/resource-naming) for more information about resource groups. - - - question: | - What are the username requirements when creating a VM? - answer: | - Usernames should be 1 - 32 characters in length. - - The following usernames are not allowed: - - - `1` - - `123` - - `a` - - `actuser` - - `adm` - - `admin` - - `admin1` - - `admin2` - - `administrator` - - `aspnet` - - `backup` - - `console` - - `david` - - `guest` - - `john` - - `owner` - - `root` - - `server` - - `sql` - - `support_388945a0` - - `support` - - `sys` - - `test` - - `test1` - - `test2` - - `test3` - - `user` - - `user1` - - `user2` - - `user3` - - `user4` - - `user5` - - `video` - - - - question: | - What are the password requirements when creating a VM? - answer: | - There are varying password length requirements, depending on the tool you are using: - - Azure portal - between 12 - 72 characters - - Azure PowerShell - between 8 - 123 characters - - Azure CLI - between 12 - 123 characters - - Azure Resource Manager (ARM) templates - 12 - 72 characters and control characters are not allowed - - - Passwords must also meet 3 out of the following 4 complexity requirements: - - * Have lower characters - * Have upper characters - * Have a digit - * Have a special character (Regex match [\W_]) - - The following passwords are not allowed: - - * abc@123 - * P@$$w0rd - * P@ssw0rd - * P@ssword123 - * Pa$$word - * pass@word1 - * Password! - * Password1 - * Password22 - * iloveyou! diff --git a/scenarios/azure-docs/articles/virtual-machines/linux/tutorial-lemp-stack.md b/scenarios/azure-docs/articles/virtual-machines/linux/tutorial-lemp-stack.md index b4e50bcc4..101666de0 100644 --- a/scenarios/azure-docs/articles/virtual-machines/linux/tutorial-lemp-stack.md +++ b/scenarios/azure-docs/articles/virtual-machines/linux/tutorial-lemp-stack.md @@ -30,42 +30,10 @@ This article walks you through how to deploy an NGINX web server, Azure MySQL Fl > * Install WordPress This setup is for quick tests or proof of concept. For more on the LEMP stack, including recommendations for a production environment, see the [Ubuntu documentation](https://help.ubuntu.com/community/ApacheMySQLPHP). -This tutorial uses the CLI within the [Azure Cloud Shell](../../cloud-shell/overview.md), which is constantly updated to the latest version. To open the Cloud Shell, select **Try it** from the top of any code block. +This tutorial uses the CLI within the [Azure Cloud Shell](/azure/cloud-shell/overview), which is constantly updated to the latest version. To open the Cloud Shell, select **Try it** from the top of any code block. If you choose to install and use the CLI locally, this tutorial requires that you're running the Azure CLI version 2.0.30 or later. Find the version by running the `az --version` command. If you need to install or upgrade, see [Install Azure CLI]( /cli/azure/install-azure-cli). -## Variable declaration - -First we need to define a few variables that help with the configuration of the LEMP workload. - -```bash -export NETWORK_PREFIX="$(($RANDOM % 254 + 1))" -export RANDOM_ID="$(openssl rand -hex 3)" -export MY_RESOURCE_GROUP_NAME="myLEMPResourceGroup$RANDOM_ID" -export REGION="westeurope" -export MY_VM_NAME="myVM$RANDOM_ID" -export MY_VM_USERNAME="azureadmin" -export MY_VM_SIZE='Standard_DS2_v2' -export MY_VM_IMAGE='Canonical:0001-com-ubuntu-minimal-jammy:minimal-22_04-lts-gen2:latest' -export MY_PUBLIC_IP_NAME="myPublicIP$RANDOM_ID" -export MY_DNS_LABEL="mydnslabel$RANDOM_ID" -export MY_NSG_NAME="myNSG$RANDOM_ID" -export MY_NSG_SSH_RULE="Allow-Access$RANDOM_ID" -export MY_VM_NIC_NAME="myVMNic$RANDOM_ID" -export MY_VNET_NAME="myVNet$RANDOM_ID" -export MY_VNET_PREFIX="10.$NETWORK_PREFIX.0.0/22" -export MY_SN_NAME="mySN$RANDOM_ID" -export MY_SN_PREFIX="10.$NETWORK_PREFIX.0.0/24" -export MY_MYSQL_DB_NAME="mydb$RANDOM_ID" -export MY_MYSQL_ADMIN_USERNAME="dbadmin$RANDOM_ID" -export MY_MYSQL_ADMIN_PW="$(openssl rand -base64 32)" -export MY_MYSQL_SN_NAME="myMySQLSN$RANDOM_ID" -export MY_WP_ADMIN_PW="$(openssl rand -base64 32)" -export MY_WP_ADMIN_USER="wpcliadmin" -export MY_AZURE_USER=$(az account show --query user.name --output tsv) -export FQDN="${MY_DNS_LABEL}.${REGION}.cloudapp.azure.com" -``` - + +```json +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367", + "location": "eastus2", + "managedBy": null, + "name": "test-rg69e367", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Create VNET + +Use [az network vnet create](/cli/azure/network/vnet#az-network-vnet-create) to create a virtual network with one subnet in the resource group: + +```bash +export RESOURCE_GROUP_NAME="test-rg$RANDOM_SUFFIX" +export VNET_NAME="vnet-1$RANDOM_SUFFIX" +export SUBNET_NAME="subnet-1$RANDOM_SUFFIX" +export VNET_ADDRESS_PREFIX="10.0.0.0/16" +export SUBNET_ADDRESS_PREFIX="10.0.0.0/24" + +az network vnet create \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $VNET_NAME \ + --address-prefix $VNET_ADDRESS_PREFIX \ + --subnet-name $SUBNET_NAME \ + --subnet-prefix $SUBNET_ADDRESS_PREFIX +``` + +Results: + + + +```json +{ + "newVNet": { + "addressSpace": { + "addressPrefixes": [ + "10.0.0.0/16" + ] + }, + "enableDdosProtection": false, + "etag": "W/\"300c6da1-ee4a-47ee-af6e-662d3a0230a1\"", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Network/virtualNetworks/vnet-169e367", + "location": "eastus2", + "name": "vnet-169e367", + "provisioningState": "Succeeded", + "resourceGroup": "test-rg69e367", + "resourceGuid": "3d64254d-70d4-47e3-a129-473d70ea2ab8", + "subnets": [ + { + "addressPrefix": "10.0.0.0/24", + "delegations": [], + "etag": "W/\"300c6da1-ee4a-47ee-af6e-662d3a0230a1\"", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Network/virtualNetworks/vnet-169e367/subnets/subnet-169e367", + "name": "subnet-169e367", + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled", + "provisioningState": "Succeeded", + "resourceGroup": "test-rg69e367", + "type": "Microsoft.Network/virtualNetworks/subnets" + } + ], + "type": "Microsoft.Network/virtualNetworks", + "virtualNetworkPeerings": [] + } +} +``` + +## Create Bastion Subnet + +Create the Bastion subnet with [az network vnet subnet create](/cli/azure/network/vnet/subnet). + +```bash +export RESOURCE_GROUP_NAME="test-rg$RANDOM_SUFFIX" +export VNET_NAME="vnet-1$RANDOM_SUFFIX" +export SUBNET_NAME="AzureBastionSubnet" +export SUBNET_ADDRESS_PREFIX="10.0.1.0/24" + +az network vnet subnet create \ + --vnet-name $VNET_NAME \ + --resource-group $RESOURCE_GROUP_NAME \ + --name AzureBastionSubnet \ + --address-prefix $SUBNET_ADDRESS_PREFIX +``` + +Results: + + + +```json +{ + "addressPrefix": "10.0.1.0/24", + "delegations": [], + "etag": "W/\"a2863964-0276-453f-a104-b37391e8088b\"", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Network/virtualNetworks/vnet-169e367/subnets/AzureBastionSubnet", + "name": "AzureBastionSubnet", + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled", + "provisioningState": "Succeeded", + "resourceGroup": "test-rg69e367", + "type": "Microsoft.Network/virtualNetworks/subnets" +} +``` + +### Create Azure Bastion + +1. Create a public IP address for the Azure Bastion host with [az network public-ip create](/cli/azure/network/public-ip). + +```bash +export RESOURCE_GROUP_NAME="test-rg$RANDOM_SUFFIX" +export PUBLIC_IP_NAME="public-ip-bastion$RANDOM_SUFFIX" +export REGION="eastus2" +export ALLOCATION_METHOD="Static" +export SKU="Standard" + +az network public-ip create \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $PUBLIC_IP_NAME \ + --location $REGION \ + --allocation-method $ALLOCATION_METHOD \ + --sku $SKU +``` + +Results: + + + +```json +{ + "publicIp": { + "ddosSettings": { + "protectionMode": "VirtualNetworkInherited" + }, + "etag": "W/\"efa750bf-63f9-4c02-9ace-a747fc405d0f\"", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Network/publicIPAddresses/public-ip-bastion69e367", + "idleTimeoutInMinutes": 4, + "ipAddress": "203.0.113.173", + "ipTags": [], + "location": "eastus2", + "name": "public-ip-bastion69e367", + "provisioningState": "Succeeded", + "publicIPAddressVersion": "IPv4", + "publicIPAllocationMethod": "Static", + "resourceGroup": "test-rg69e367", + "resourceGuid": "fc809493-80c8-482c-9f5a-9d6442472a99", + "sku": { + "name": "Standard", + "tier": "Regional" + }, + "type": "Microsoft.Network/publicIPAddresses" + } +} +``` + +## Create Azure Bastion Host + +Create an Azure Bastion host with [az network bastion create](/cli/azure/network/bastion). Azure Bastion is used to securely connect Azure virtual machines without exposing them to the public internet. + +```bash +export RESOURCE_GROUP_NAME="test-rg$RANDOM_SUFFIX" +export BASTION_NAME="bastion$RANDOM_SUFFIX" +export VNET_NAME="vnet-1$RANDOM_SUFFIX" +export PUBLIC_IP_NAME="public-ip-bastion$RANDOM_SUFFIX" +export REGION="eastus2" + +az network bastion create \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $BASTION_NAME \ + --vnet-name $VNET_NAME \ + --public-ip-address $PUBLIC_IP_NAME \ + --location $REGION +``` + +Results: + + + +```json +{ + "disableCopyPaste": false, + "dnsName": "bst-cc1d5c1d-9496-44fa-a8b3-3b2130efa306.bastion.azure.com", + "enableFileCopy": false, + "enableIpConnect": false, + "enableKerberos": false, + "enableSessionRecording": false, + "enableShareableLink": false, + "enableTunneling": false, + "etag": "W/\"229bd068-160b-4935-b23d-eddce4bb31ed\"", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Network/bastionHosts/bastion69e367", + "ipConfigurations": [ + { + "etag": "W/\"229bd068-160b-4935-b23d-eddce4bb31ed\"", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Network/bastionHosts/bastion69e367/bastionHostIpConfigurations/bastion_ip_config", + "name": "bastion_ip_config", + "privateIPAllocationMethod": "Dynamic", + "provisioningState": "Succeeded", + "publicIPAddress": { + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Network/publicIPAddresses/public-ip-bastion69e367", + "resourceGroup": "test-rg69e367" + }, + "resourceGroup": "test-rg69e367", + "subnet": { + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Network/virtualNetworks/vnet-169e367/subnets/AzureBastionSubnet", + "resourceGroup": "test-rg69e367" + }, + "type": "Microsoft.Network/bastionHosts/bastionHostIpConfigurations" + } + ], + "location": "eastus2", + "name": "bastion69e367", + "provisioningState": "Succeeded", + "resourceGroup": "test-rg69e367", + "scaleUnits": 2, + "sku": { + "name": "Standard" + }, + "type": "Microsoft.Network/bastionHosts" +} +``` + +## Create a network interface with Accelerated Networking + +1. Use [az network nic create](/cli/azure/network/nic#az-network-nic-create) to create a network interface (NIC) with Accelerated Networking enabled. The following example creates a NIC in the subnet of the virtual network. + + ```bash + export RESOURCE_GROUP_NAME="test-rg$RANDOM_SUFFIX" + export NIC_NAME="nic-1$RANDOM_SUFFIX" + export VNET_NAME="vnet-1$RANDOM_SUFFIX" + export SUBNET_NAME="subnet-1$RANDOM_SUFFIX" + + az network nic create \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $NIC_NAME \ + --vnet-name $VNET_NAME \ + --subnet $SUBNET_NAME \ + --accelerated-networking true + ``` + + Results: + + + + ```json + { + "NewNIC": { + "auxiliaryMode": "None", + "auxiliarySku": "None", + "disableTcpStateTracking": false, + "dnsSettings": { + "appliedDnsServers": [], + "dnsServers": [], + "internalDomainNameSuffix": "juswipouodrupijji24xb0rkxa.cx.internal.cloudapp.net" + }, + "enableAcceleratedNetworking": true, + "enableIPForwarding": false, + "etag": "W/\"0e24b553-769b-4350-b1aa-ab4cd04100bf\"", + "hostedWorkloads": [], + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Network/networkInterfaces/nic-169e367", + "ipConfigurations": [ + { + "etag": "W/\"0e24b553-769b-4350-b1aa-ab4cd04100bf\"", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Network/networkInterfaces/nic-169e367/ipConfigurations/ipconfig1", + "name": "ipconfig1", + "primary": true, + "privateIPAddress": "10.0.0.4", + "privateIPAddressVersion": "IPv4", + "privateIPAllocationMethod": "Dynamic", + "provisioningState": "Succeeded", + "resourceGroup": "test-rg69e367", + "subnet": { + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Network/virtualNetworks/vnet-169e367/subnets/subnet-169e367", + "resourceGroup": "test-rg69e367" + }, + "type": "Microsoft.Network/networkInterfaces/ipConfigurations" + } + ], + "location": "eastus2", + "name": "nic-169e367", + "nicType": "Standard", + "provisioningState": "Succeeded", + "resourceGroup": "test-rg69e367", + "resourceGuid": "6798a335-bd66-42cc-a92a-bb678d4d146e", + "tapConfigurations": [], + "type": "Microsoft.Network/networkInterfaces", + "vnetEncryptionSupported": false + } + } + ``` + +--- + +## Create a VM and attach the NIC + +Use [az vm create](/cli/azure/vm#az-vm-create) to create the VM, and use the `--nics` option to attach the NIC you created. Ensure you select a VM size and distribution listed in [Windows and Linux Accelerated Networking](https://azure.microsoft.com/updates/accelerated-networking-in-expanded-preview). For a list of all VM sizes and characteristics, see [Sizes for virtual machines in Azure](/azure/virtual-machines/sizes). The following example creates a VM with a size that supports Accelerated Networking, Standard_DS4_v2. The command will generate SSH keys for the virtual machine for login. Make note of the location of the private key. The private key is needed in later steps for connecting to the virtual machine with Azure Bastion. + +```bash +export RESOURCE_GROUP_NAME="test-rg$RANDOM_SUFFIX" +export VM_NAME="vm-1$RANDOM_SUFFIX" +export IMAGE="Ubuntu2204" +export SIZE="Standard_DS4_v2" +export ADMIN_USER="azureuser" +export NIC_NAME="nic-1$RANDOM_SUFFIX" + +az vm create \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $VM_NAME \ + --image $IMAGE \ + --size $SIZE \ + --admin-username $ADMIN_USER \ + --generate-ssh-keys \ + --nics $NIC_NAME +``` + +Results: + + + +```json +{ + "fqdns": "", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/test-rg69e367/providers/Microsoft.Compute/virtualMachines/vm-169e367", + "location": "eastus2", + "macAddress": "60-45-BD-84-F0-D5", + "powerState": "VM running", + "privateIpAddress": "10.0.0.4", + "publicIpAddress": "", + "resourceGroup": "test-rg69e367", + "zones": "" +} +``` + +## Next steps + +- [How Accelerated Networking works in Linux and FreeBSD VMs](./accelerated-networking-how-it-works.md) + +- [Proximity placement groups](/azure/virtual-machines/co-location) \ No newline at end of file diff --git a/azure-vote-start.yml b/scenarios/azure-management-docs/articles/azure-linux/aks-store-quickstart.yaml similarity index 74% rename from azure-vote-start.yml rename to scenarios/azure-management-docs/articles/azure-linux/aks-store-quickstart.yaml index fabe2db67..179a961c1 100644 --- a/azure-vote-start.yml +++ b/scenarios/azure-management-docs/articles/azure-linux/aks-store-quickstart.yaml @@ -1,8 +1,9 @@ apiVersion: apps/v1 -kind: Deployment +kind: StatefulSet metadata: name: rabbitmq spec: + serviceName: rabbitmq replicas: 1 selector: matchLabels: @@ -47,12 +48,12 @@ spec: path: enabled_plugins --- apiVersion: v1 -data: - rabbitmq_enabled_plugins: | - [rabbitmq_management,rabbitmq_prometheus,rabbitmq_amqp1_0]. kind: ConfigMap metadata: name: rabbitmq-enabled-plugins +data: + rabbitmq_enabled_plugins: | + [rabbitmq_management,rabbitmq_prometheus,rabbitmq_amqp1_0]. --- apiVersion: v1 kind: Service @@ -111,6 +112,27 @@ spec: limits: cpu: 75m memory: 128Mi + startupProbe: + httpGet: + path: /health + port: 3000 + failureThreshold: 5 + initialDelaySeconds: 20 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /health + port: 3000 + failureThreshold: 3 + initialDelaySeconds: 3 + periodSeconds: 5 + livenessProbe: + httpGet: + path: /health + port: 3000 + failureThreshold: 5 + initialDelaySeconds: 3 + periodSeconds: 3 initContainers: - name: wait-for-rabbitmq image: busybox @@ -157,13 +179,30 @@ spec: image: ghcr.io/azure-samples/aks-store-demo/product-service:latest ports: - containerPort: 3002 + env: + - name: AI_SERVICE_URL + value: "http://ai-service:5001/" resources: requests: cpu: 1m memory: 1Mi limits: - cpu: 1m - memory: 7Mi + cpu: 2m + memory: 20Mi + readinessProbe: + httpGet: + path: /health + port: 3002 + failureThreshold: 3 + initialDelaySeconds: 3 + periodSeconds: 5 + livenessProbe: + httpGet: + path: /health + port: 3002 + failureThreshold: 5 + initialDelaySeconds: 3 + periodSeconds: 3 --- apiVersion: v1 kind: Service @@ -212,6 +251,27 @@ spec: limits: cpu: 1000m memory: 512Mi + startupProbe: + httpGet: + path: /health + port: 8080 + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 5 + readinessProbe: + httpGet: + path: /health + port: 8080 + failureThreshold: 3 + initialDelaySeconds: 3 + periodSeconds: 3 + livenessProbe: + httpGet: + path: /health + port: 8080 + failureThreshold: 5 + initialDelaySeconds: 3 + periodSeconds: 3 --- apiVersion: v1 kind: Service @@ -223,4 +283,4 @@ spec: targetPort: 8080 selector: app: store-front - type: LoadBalancer + type: LoadBalancer \ No newline at end of file diff --git a/scenarios/azure-management-docs/articles/azure-linux/quickstart-azure-cli.md b/scenarios/azure-management-docs/articles/azure-linux/quickstart-azure-cli.md new file mode 100644 index 000000000..39db87e1a --- /dev/null +++ b/scenarios/azure-management-docs/articles/azure-linux/quickstart-azure-cli.md @@ -0,0 +1,458 @@ +--- +title: 'Quickstart: Deploy an Azure Linux Container Host for AKS cluster by using the Azure CLI' +description: Learn how to quickly create an Azure Linux Container Host for AKS cluster using the Azure CLI. +author: suhuruli +ms.author: suhuruli +ms.service: microsoft-linux +ms.custom: references_regions, devx-track-azurecli, linux-related-content, innovation-engine +ms.topic: quickstart +ms.date: 04/18/2023 +--- + +# Quickstart: Deploy an Azure Linux Container Host for AKS cluster by using the Azure CLI + +Get started with the Azure Linux Container Host by using the Azure CLI to deploy an Azure Linux Container Host for AKS cluster. After installing the prerequisites, you will create a resource group, create an AKS cluster, connect to the cluster, and run a sample multi-container application in the cluster. + +## Prerequisites + +- [!INCLUDE [quickstarts-free-trial-note](~/reusable-content/ce-skilling/azure/includes/quickstarts-free-trial-note.md)] +- Use the Bash environment in [Azure Cloud Shell](/azure/cloud-shell/overview). For more information, see [Azure Cloud Shell Quickstart - Bash](/azure/cloud-shell/quickstart). + + :::image type="icon" source="~/reusable-content/ce-skilling/azure/media/cloud-shell/launch-cloud-shell-button.png" border="false" link="https://portal.azure.com/#cloudshell/"::: + +- If you prefer to run CLI reference commands locally, [install](/cli/azure/install-azure-cli) the Azure CLI. If you're running on Windows or macOS, consider running Azure CLI in a Docker container. For more information, see [How to run the Azure CLI in a Docker container](/cli/azure/run-azure-cli-docker). + + - If you're using a local installation, sign in to the Azure CLI by using the [az login](/cli/azure/reference-index#az-login) command. To finish the authentication process, follow the steps displayed in your terminal. For other sign-in options, see [Sign in with the Azure CLI](/cli/azure/authenticate-azure-cli). + - When you're prompted, install the Azure CLI extension on first use. For more information about extensions, see [Use extensions with the Azure CLI](/cli/azure/azure-cli-extensions-overview). + - Run [`az version`](/cli/azure/reference-index?#az-version) to find the version and dependent libraries that are installed. To upgrade to the latest version, run [az upgrade](/cli/azure/reference-index?#az-upgrade). + +## Create a resource group + +An Azure resource group is a logical group in which Azure resources are deployed and managed. When creating a resource group, it is required to specify a location. This location is: + +- The storage location of your resource group metadata. +- Where your resources will run in Azure if you don't specify another region when creating a resource. + +Create a resource group using the `az group create` command. + +```azurecli-interactive +export RANDOM_ID="$(openssl rand -hex 3)" +export MY_RESOURCE_GROUP_NAME="myAzureLinuxResourceGroup$RANDOM_ID" +export REGION="westeurope" + +az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION +``` + +Results: + +```JSON +{ + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/$MY_RESOURCE_GROUP_NAMExxxxxx", + "location": "$REGION", + "managedBy": null, + "name": "$MY_RESOURCE_GROUP_NAME", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +## Create an Azure Linux Container Host cluster + +Create an AKS cluster using the `az aks create` command with the `--os-sku` parameter to provision the AKS cluster with an Azure Linux image. + +```azurecli-interactive +export MY_AZ_CLUSTER_NAME="myAzureLinuxCluster$RANDOM_ID" +az aks create --name $MY_AZ_CLUSTER_NAME --resource-group $MY_RESOURCE_GROUP_NAME --os-sku AzureLinux +``` + +After a few minutes, the command completes and returns JSON-formatted information about the cluster. + +## Connect to the cluster + +To manage a Kubernetes cluster, use the Kubernetes command-line client, `kubectl`. `kubectl` is already installed if you use Azure Cloud Shell. To install `kubectl` locally, use the `az aks install-cli` command. + +1. Configure `kubectl` to connect to your Kubernetes cluster using the `az aks get-credentials` command. This command downloads credentials and configures the Kubernetes CLI to use them. + + ```azurecli-interactive + az aks get-credentials --resource-group $MY_RESOURCE_GROUP_NAME --name $MY_AZ_CLUSTER_NAME + kubectl get nodes + ``` + +## Deploy the application + +To deploy the application, you use a manifest file to create all the objects required to run the [AKS Store application](https://github.com/Azure-Samples/aks-store-demo). A Kubernetes manifest file defines a cluster's desired state, such as which container images to run. The manifest includes the following Kubernetes deployments and services: +- **Store front**: Web application for customers to view products and place orders. +- **Product service**: Shows product information. +- **Order service**: Places orders. +- **Rabbit MQ**: Message queue for an order queue. +NOTE: We don't recommend running stateful containers, such as Rabbit MQ, without persistent storage for production. These are used here for simplicity, but we recommend using managed services, such as Azure CosmosDB or Azure Service Bus. + +```bash +cat < aks-store-quickstart.yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: rabbitmq +spec: + serviceName: rabbitmq + replicas: 1 + selector: + matchLabels: + app: rabbitmq + template: + metadata: + labels: + app: rabbitmq + spec: + nodeSelector: + "kubernetes.io/os": linux + containers: + - name: rabbitmq + image: mcr.microsoft.com/mirror/docker/library/rabbitmq:3.10-management-alpine + ports: + - containerPort: 5672 + name: rabbitmq-amqp + - containerPort: 15672 + name: rabbitmq-http + env: + - name: RABBITMQ_DEFAULT_USER + value: "username" + - name: RABBITMQ_DEFAULT_PASS + value: "password" + resources: + requests: + cpu: 10m + memory: 128Mi + limits: + cpu: 250m + memory: 256Mi + volumeMounts: + - name: rabbitmq-enabled-plugins + mountPath: /etc/rabbitmq/enabled_plugins + subPath: enabled_plugins + volumes: + - name: rabbitmq-enabled-plugins + configMap: + name: rabbitmq-enabled-plugins + items: + - key: rabbitmq_enabled_plugins + path: enabled_plugins +--- +apiVersion: v1 +data: + rabbitmq_enabled_plugins: | + [rabbitmq_management,rabbitmq_prometheus,rabbitmq_amqp1_0]. +kind: ConfigMap +metadata: + name: rabbitmq-enabled-plugins +--- +apiVersion: v1 +kind: Service +metadata: + name: rabbitmq +spec: + selector: + app: rabbitmq + ports: + - name: rabbitmq-amqp + port: 5672 + targetPort: 5672 + - name: rabbitmq-http + port: 15672 + targetPort: 15672 + type: ClusterIP +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: order-service +spec: + replicas: 1 + selector: + matchLabels: + app: order-service + template: + metadata: + labels: + app: order-service + spec: + nodeSelector: + "kubernetes.io/os": linux + containers: + - name: order-service + image: ghcr.io/azure-samples/aks-store-demo/order-service:latest + ports: + - containerPort: 3000 + env: + - name: ORDER_QUEUE_HOSTNAME + value: "rabbitmq" + - name: ORDER_QUEUE_PORT + value: "5672" + - name: ORDER_QUEUE_USERNAME + value: "username" + - name: ORDER_QUEUE_PASSWORD + value: "password" + - name: ORDER_QUEUE_NAME + value: "orders" + - name: FASTIFY_ADDRESS + value: "0.0.0.0" + resources: + requests: + cpu: 1m + memory: 50Mi + limits: + cpu: 75m + memory: 128Mi + startupProbe: + httpGet: + path: /health + port: 3000 + failureThreshold: 5 + initialDelaySeconds: 20 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /health + port: 3000 + failureThreshold: 3 + initialDelaySeconds: 3 + periodSeconds: 5 + livenessProbe: + httpGet: + path: /health + port: 3000 + failureThreshold: 5 + initialDelaySeconds: 3 + periodSeconds: 3 + initContainers: + - name: wait-for-rabbitmq + image: busybox + command: ['sh', '-c', 'until nc -zv rabbitmq 5672; do echo waiting for rabbitmq; sleep 2; done;'] + resources: + requests: + cpu: 1m + memory: 50Mi + limits: + cpu: 75m + memory: 128Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: order-service +spec: + type: ClusterIP + ports: + - name: http + port: 3000 + targetPort: 3000 + selector: + app: order-service +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: product-service +spec: + replicas: 1 + selector: + matchLabels: + app: product-service + template: + metadata: + labels: + app: product-service + spec: + nodeSelector: + "kubernetes.io/os": linux + containers: + - name: product-service + image: ghcr.io/azure-samples/aks-store-demo/product-service:latest + ports: + - containerPort: 3002 + env: + - name: AI_SERVICE_URL + value: "http://ai-service:5001/" + resources: + requests: + cpu: 1m + memory: 1Mi + limits: + cpu: 2m + memory: 20Mi + readinessProbe: + httpGet: + path: /health + port: 3002 + failureThreshold: 3 + initialDelaySeconds: 3 + periodSeconds: 5 + livenessProbe: + httpGet: + path: /health + port: 3002 + failureThreshold: 5 + initialDelaySeconds: 3 + periodSeconds: 3 +--- +apiVersion: v1 +kind: Service +metadata: + name: product-service +spec: + type: ClusterIP + ports: + - name: http + port: 3002 + targetPort: 3002 + selector: + app: product-service +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: store-front +spec: + replicas: 1 + selector: + matchLabels: + app: store-front + template: + metadata: + labels: + app: store-front + spec: + nodeSelector: + "kubernetes.io/os": linux + containers: + - name: store-front + image: ghcr.io/azure-samples/aks-store-demo/store-front:latest + ports: + - containerPort: 8080 + name: store-front + env: + - name: VUE_APP_ORDER_SERVICE_URL + value: "http://order-service:3000/" + - name: VUE_APP_PRODUCT_SERVICE_URL + value: "http://product-service:3002/" + resources: + requests: + cpu: 1m + memory: 200Mi + limits: + cpu: 1000m + memory: 512Mi + startupProbe: + httpGet: + path: /health + port: 8080 + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 5 + readinessProbe: + httpGet: + path: /health + port: 8080 + failureThreshold: 3 + initialDelaySeconds: 3 + periodSeconds: 3 + livenessProbe: + httpGet: + path: /health + port: 8080 + failureThreshold: 5 + initialDelaySeconds: 3 + periodSeconds: 3 +--- +apiVersion: v1 +kind: Service +metadata: + name: store-front +spec: + ports: + - port: 80 + targetPort: 8080 + selector: + app: store-front + type: LoadBalancer +EOF +kubectl apply -f aks-store-quickstart.yaml +``` + +## Wait for cluster to startup + +Wait for cluster to finish spinning up + +```azurecli-interactive +runtime="5 minutes" +endtime=$(date -ud "$runtime" +%s) +while [[ $(date -u +%s) -le $endtime ]] +do + STATUS=$(kubectl get pods -l app=store-front -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') + echo $STATUS + if [ "$STATUS" == 'True' ] + then + export IP_ADDRESS=$(kubectl get service store-front --output 'jsonpath={..status.loadBalancer.ingress[0].ip}') + echo "Service IP Address: $IP_ADDRESS" + if [ -n "$IP_ADDRESS" ]; then + break + else + echo "Waiting for IP address..." + fi + else + sleep 10 + fi +done +``` + +## Test the application + +You can validate that the application is running by visiting the public IP address or the application URL. + +Get the application URL using the following commands: + +```azurecli-interactive +curl "http://$IP_ADDRESS" +``` + +Results: + +```HTML + + + + + + + + store-front + + + + + +
+ + +``` + +```OUTPUT +echo "You can now visit your web server at $IP_ADDRESS" +``` + +## Delete the cluster + +If you no longer need them, you can clean up unnecessary resources to avoid Azure charges. You can remove the resource group, container service, and all related resources using the `az group delete` command. + +## Next steps + +In this quickstart, you deployed an Azure Linux Container Host cluster. To learn more about the Azure Linux Container Host, and walk through a complete cluster deployment and management example, continue to the Azure Linux Container Host tutorial. + +> [!div class="nextstepaction"] +> [Azure Linux Container Host tutorial](./tutorial-azure-linux-create-cluster.md) + + +[kubectl-apply]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#apply \ No newline at end of file diff --git a/scenarios/metadata.json b/scenarios/metadata.json index 5dfa2d3df..908efc672 100644 --- a/scenarios/metadata.json +++ b/scenarios/metadata.json @@ -2,10 +2,10 @@ { "status": "active", "key": "azure-docs/articles/aks/learn/quick-kubernetes-deploy-cli.md", - "title": "Quickstart: Deploy an Azure Kubernetes Service (AKS) cluster using Azure CLI", + "title": "Deploy an Azure Kubernetes Service (AKS) cluster", "description": "Learn how to quickly deploy a Kubernetes cluster and deploy an application in Azure Kubernetes Service (AKS) using Azure CLI", "stackDetails": "", - "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-docs/articles/aks/learn/quick-kubernetes-deploy-cli.md", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-aks-docs/articles/aks/learn/quick-kubernetes-deploy-cli.md", "documentationUrl": "https://learn.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-cli", "nextSteps": [ { @@ -65,10 +65,85 @@ ] } }, + { + "status": "active", + "key": "azure-databases-docs/articles/mysql/flexible-server/tutorial-deploy-wordpress-on-aks.md", + "title": "Tutorial: Deploy WordPress on AKS cluster by using Azure CLI", + "description": "Learn how to quickly build and deploy WordPress on AKS with Azure Database for MySQL - Flexible Server.", + "stackDetails": [ + "An Ubuntu 22.04 Linux VM (Standard DS2_v2)", + "Azure Database for MySQL - Flexible Server: 8.0.21", + "NGINX web server", + "PHP version 8.1-fpm", + "Latest WordPress", + "Network interface with public IP and network security group", + "Azure Private DNS Zone for Azure MySQL Flexible Server", + "Use port 22 for SSH and ports 80, 443 for web traffic" + ], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-databases-docs/articles/mysql/flexible-server/tutorial-deploy-wordpress-on-aks.md", + "documentationUrl": "https://learn.microsoft.com/en-us/azure/mysql/flexible-server/tutorial-deploy-wordpress-on-aks", + "nextSteps": [ + { + "title": "Access the Kubernetes web dashboard", + "url": "https://learn.microsoft.com/en-us/azure/aks/kubernetes-dashboard" + }, + { + "title": "Scale your AKS cluster", + "url": "https://learn.microsoft.com/en-us/azure/aks/tutorial-kubernetes-scale" + }, + { + "title": "Manage your Azure Database for MySQL flexible server instance", + "url": "https://learn.microsoft.com/en-us/azure/mysql/flexible-server/quickstart-create-server-cli" + }, + { + "title": "Configure server parameters for your database server", + "url": "https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-configure-server-parameters-cli" + } + ], + "configurations": { + "permissions": [ + "Microsoft.Resources/resourceGroups/write", + "Microsoft.Network/virtualNetworks/write", + "Microsoft.Network/publicIPAddresses/write", + "Microsoft.Network/networkSecurityGroups/write", + "Microsoft.Network/networkSecurityGroups/securityRules/write", + "Microsoft.Network/networkInterfaces/write", + "Microsoft.Network/networkInterfaces/ipConfigurations/write", + "Microsoft.Storage/storageAccounts/write", + "Microsoft.Network/privateDnsZones/write", + "Microsoft.Network/privateDnsZones/virtualNetworkLinks/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/A/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/TXT/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/SRV/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/CNAME/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/MX/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/AAAA/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/PTR/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/CERT/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/NS/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/SOA/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/CAA/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/ANY/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/SSHFP/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/SPF/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/DNSKEY/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/DS/write", + "Microsoft.Network/privateDnsZones/privateDnsRecordSets/NAPTR/write", + "Microsoft.Compute/virtualMachines/write", + "Microsoft.Compute/virtualMachines/extensions/write", + "Microsoft.Compute/virtualMachines/read", + "Microsoft.Authorization/roleAssignments/write", + "Microsoft.Authorization/roleAssignments/read", + "Microsoft.Authorization/roleDefinitions/read", + "Microsoft.Authorization/roleDefinitions/write" + ] + } + }, { "status": "active", "key": "azure-docs/articles/static-web-apps/get-started-cli.md", - "title": "Quickstart: Building your first static site with the Azure Static Web Apps using the CLI", + "title": "Deploy a Static site with the Azure Static Web Apps", "description": "Learn to deploy a static site to Azure Static Web Apps with the Azure CLI.", "stackDetails": "", "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-docs/articles/static-web-apps/get-started-cli.md", @@ -79,7 +154,8 @@ "url": "https://learn.microsoft.com/en-us/azure/static-web-apps/add-api" } ], - "configurations": {} + "configurations": { + } }, { "status": "active", @@ -103,12 +179,13 @@ "url": "https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/tutorial-autoscale-cli" } ], - "configurations": {} + "configurations": { + } }, { "status": "active", "key": "azure-docs/articles/virtual-machines/linux/quick-create-cli.md", - "title": "Quickstart: Use the Azure CLI to create a Linux Virtual Machine", + "title": "Deploy a Linux virtual machine", "description": "In this quickstart, you learn how to use the Azure CLI to create a Linux virtual machine", "stackDetails": [ "An Ubuntu 22.04 Linux VM (Standard DS1_v2)", @@ -159,7 +236,23 @@ "stackDetails": "", "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-docs/articles/virtual-machines/linux/tutorial-lemp-stack.md", "documentationUrl": "https://learn.microsoft.com/en-us/azure/virtual-machines/linux/tutorial-lemp-stack", - "configurations": {} + "nextSteps": [ + { + "title": "Learn about virtual machines", + "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/" + }, + { + "title": "Create and manage Linux VMs with the Azure CLI", + "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/linux/tutorial-manage-vm" + }, + { + "title": "Secure your Linux VM", + "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/linux/tutorial-secure-vm" + } + + ], + "configurations": { + } }, { "status": "active", @@ -183,7 +276,8 @@ "url": "https://go.microsoft.com/fwlink/p/?linkid=2259865" } ], - "configurations": {} + "configurations": { + } }, { "status": "active", @@ -211,7 +305,8 @@ "url": "https://learn.microsoft.com/azure/aks/tutorial-kubernetes-app-update?tabs=azure-cli" } ], - "configurations": {} + "configurations": { + } }, { "status": "active", @@ -221,7 +316,26 @@ "stackDetails": "", "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/CreateRHELVMAndSSH/create-rhel-vm-ssh.md", "documentationUrl": "", - "configurations": {} + "nextSteps": [ + { + "title": "Learn about virtual machines", + "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/" + }, + { + "title": "Create an Ubuntu Virtual Machine", + "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/linux/quick-create-cli" + }, + { + "title": "Create custom VM images", + "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/linux/tutorial-custom-images" + }, + { + "title": "Load Balance VMs", + "url": "https://learn.microsoft.com/en-us/azure/load-balancer/quickstart-load-balancer-standard-public-cli" + } + ], + "configurations": { + } }, { "status": "active", @@ -253,24 +367,568 @@ "permissions": [] } }, + { + "status": "inactive", + "key": "azure-aks-docs/articles/aks/workload-identity-deploy-cluster.md", + "title": "Deploy and configure an AKS cluster with workload identity", + "description": "In this Azure Kubernetes Service (AKS) article, you deploy an Azure Kubernetes Service cluster and configure it with a Microsoft Entra Workload ID.", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-aks-docs/articles/aks/workload-identity-deploy-cluster.md", + "documentationUrl": "", + "nextSteps": [ + { + "title": "Kubectl Describe Command Reference", + "url": "https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#describe" + } + ], + "configurations": { + "permissions": [] + } + }, + { + "status": "active", + "key": "ObtainPerformanceMetricsLinuxSustem/obtain-performance-metrics-linux-system.md", + "title": "Obtaining Performance metrics from a Linux system", + "description": "Learn how to obtainer Performance metrics from a Linux system.", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/ObtainPerformanceMetricsLinuxSustem/obtain-performance-metrics-linux-system.md", + "documentationUrl": "", + "configurations": { + "permissions": [], + "configurableParams": [ + { + "inputType": "textInput", + "commandKey": "MY_RESOURCE_GROUP_NAME", + "title": "Resource Group Name", + "defaultValue": "" + }, + { + "inputType": "textInput", + "commandKey": "MY_VM_NAME", + "title": "VM Name", + "defaultValue": "" + } + ] + } + }, + { + "status": "inactive", + "key": "azure-aks-docs/articles/aks/create-postgresql-ha.md", + "title": "Create infrastructure for deploying a highly available PostgreSQL database on AKS", + "description": "Create the infrastructure needed to deploy a highly available PostgreSQL database on AKS using the CloudNativePG operator.", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-aks-docs/articles/aks/create-postgresql-ha.md", + "documentationUrl": "", + "nextSteps": [ + { + "title": "Deploy a highly available PostgreSQL database on AKS with Azure CLI", + "url": "https://learn.microsoft.com/en-us/azure/aks/deploy-postgresql-ha?tabs=helm" + } + + ], + "configurations": { + } + }, + { + "status": "inactive", + "key": "azure-aks-docs/articles/aks/deploy-postgresql-ha.md", + "title": "Deploy a highly available PostgreSQL database on AKS with Azure CLI", + "description": "In this article, you deploy a highly available PostgreSQL database on AKS using the CloudNativePG operator.", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-aks-docs/articles/aks/deploy-postgresql-ha.md", + "documentationUrl": "", + "configurations": { + } + }, + { + "status": "inactive", + "key": "azure-aks-docs/articles/aks/postgresql-ha-overview.md", + "title": "Overview of deploying a highly available PostgreSQL database on AKS with Azure CLI", + "description": "Learn how to deploy a highly available PostgreSQL database on AKS using the CloudNativePG operator.", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-aks-docs/articles/aks/postgresql-ha-overview.md", + "documentationUrl": "", + "configurations": { + } + }, + { + "status": "inactive", + "key": "CreateContainerAppDeploymentFromSource/create-container-app-deployment-from-source.md", + "title": "Create a Container App leveraging Blob Store, SQL, and Computer Vision", + "description": "This tutorial shows how to create a Container App leveraging Blob Store, SQL, and Computer Vision", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/CreateContainerAppDeploymentFromSource/create-container-app-deployment-from-source.md", + "documentationUrl": "", + "nextSteps": [ + { + "title": "Azure Container Apps documentation", + "url": "https://learn.microsoft.com/azure/container-apps/" + }, + { + "title": "Azure Database for PostgreSQL documentation", + "url": "https://learn.microsoft.com/azure/postgresql/" + }, + { + "title": "Azure Blob Storage documentation", + "url": "https://learn.microsoft.com/azure/storage/blobs/" + }, + { + "title": "Azure Computer (AI) Vision Documentation", + "url": "https://learn.microsoft.com/azure/ai-services/computer-vision/" + } + ], + "configurations": { + } + }, + { + "status": "inactive", + "key": "BlobVisionOnAKS/blob-vision-aks.md" + }, + { + "status": "inactive", + "key": "DeployHAPGonARO/deploy-ha-pg-on-aro.md", + "title": "Create a Highly Available PostgreSQL Cluster on Azure Red Hat OpenShift", + "description": "This tutorial shows how to create a Highly Available PostgreSQL cluster on Azure Red Hat OpenShift (ARO) using the CloudNativePG operator", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/DeployHAPGonARO/deploy-ha-pg-aro.md", + "documentationUrl": "", + "configurations": { + } + }, + { + "status": "active", + "key": "AIChatApp/ai-chat-app.md", + "title": "Create an Azure OpenAI, LangChain, ChromaDB, and Chainlit Chat App in Container Apps", + "description": "", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/AIChatApp/ai-chat-app.md", + "documentationUrl": "", + "nextSteps": [], + "configurations": { + "permissions": [] + } + }, + { + "status": "active", + "key": "ConfigurePythonContainer/configure-python-container.md", + "title": "Configure Linux Python apps", + "description": "Learn how to configure the Python container in which web apps are run, using both the Azure portal and the Azure CLI.", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/ConfigurePythonContainer/configure-python-container.md", + "documentationUrl": "https://learn.microsoft.com/en-us/azure/app-service/configure-language-python", + "nextSteps": [], + "configurations": { + "permissions": [] + } + }, + { + "status": "active", + "key": "CreateSpeechService/create-speech-service.md", + "title": "Quickstart: The Speech CLI - Speech service", + "description": "In this Azure AI Speech CLI quickstart, you interact with speech to text, text to speech, and speech translation without having to write code.", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/CreateSpeechService/create-speech-service.md", + "documentationUrl": "https://learn.microsoft.com/en-us/azure/ai-services/speech-service/spx-basics?tabs=windowsinstall%2Cterminal", + "nextSteps": [], + "configurations": { + "permissions": [] + } + }, + { + "status": "inactive", + "key": "azure-aks-docs/articles/aks/airflow-create-infrastructure.md", + "title": "Create the infrastructure for deploying Apache Airflow on Azure Kubernetes Service (AKS)", + "description": "In this article, you create the infrastructure needed to deploy Apache Airflow on Azure Kubernetes Service (AKS) using Helm.", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-aks-docs/articles/aks/airflow-create-infrastructure.md", + "documentationUrl": "", + "nextSteps": [ + { + "title": "Deploy Apache Airflow on AKS", + "url": "https://learn.microsoft.com/en-us/azure/aks/airflow-deploy" + } + ], + "configurations": { + "permissions": [] + } + }, + { + "status": "inactive", + "key": "azure-aks-docs/articles/aks/airflow-deploy.md", + "title": "Configure and deploy Apache Airflow on Azure Kubernetes Service (AKS)", + "description": "In this article, you create the infrastructure needed to deploy Apache Airflow on Azure Kubernetes Service (AKS) using Helm.", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-aks-docs/articles/aks/airflow-deploy.md", + "documentationUrl": "", + "nextSteps": [ + { + "title": "Deploy a MongoDB cluster on Azure Kubernetes Service (AKS)", + "url": "https://learn.microsoft.com/en-us/azure/aks/mongodb-overview" + }, + { + "title": "Deploy a highly available PostgreSQL database on Azure Kubernetes Service (AKS)", + "url": "https://learn.microsoft.com/en-us/azure/aks/postgresql-ha-overview" + }, + { + "title": "Deploy a Valkey cluster on Azure Kubernetes Service (AKS)", + "url": "https://learn.microsoft.com/en-us/azure/aks/valkey-overview" + } + + ], + "configurations": { + "permissions": [], + "configurableParams": [ + { + "inputType": "textInput", + "commandKey": "MY_RESOURCE_GROUP_NAME", + "title": "Resource Group Name", + "defaultValue": "" + }, + { + "inputType": "textInput", + "commandKey": "MY_CLUSTER_NAME", + "title": "AKS Cluster Name", + "defaultValue": "" + } + ] + } + }, + { + "status": "active", + "key": "DeployPremiumSSDV2/deploy-premium-ssd-v2.md", + "title": "Deploy a Premium SSD v2 managed disk", + "description": "Learn how to deploy a Premium SSD v2 and about its regional availability.", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/DeployPremiumSSDV2/deploy-premium-ssd-v2.md", + "documentationUrl": "https://learn.microsoft.com/en-us/azure/virtual-machines/disks-deploy-premium-v2?tabs=azure-cli", + "nextSteps": [], + "configurations": { + "permissions": [] + } + }, + { + "status": "inactive", + "key": "GPUNodePoolAKS/gpu-node-pool-aks.md", + "title": "Create a multi-instance GPU node pool in Azure Kubernetes Service (AKS)", + "description": "Learn how to create a multi-instance GPU node pool in Azure Kubernetes Service (AKS).", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/GPUNodePoolAKS/gpu-node-pool-aks.md", + "documentationUrl": "https://learn.microsoft.com/en-us/azure/aks/gpu-multi-instance?tabs=azure-cli", + "nextSteps": [], + "configurations": { + "permissions": [] + } + }, { "status": "active", "key": "PostgresRagLlmDemo/README.md", - "title": "Quickstart: Deploy a Postgres vector database", - "description": "Setup a Postgres vector database and openai resources to run a RAG-LLM model", + "title": "Deploy a Postgres vector database", + "description": "Set up a Postgres vector database and openai resources to run a RAG-LLM model.", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/PostgresRagLlmDemo/README.md", + "documentationUrl": "", + "nextSteps": [], + "configurations": { + "permissions": [] + } + }, + { + "status": "active", + "key": "CreateAOAIDeployment/create-aoai-deployment.md", + "title": "Create and manage Azure OpenAI Service deployments with the Azure CLI", + "description": "Learn how to use the Azure CLI to create an Azure OpenAI resource and manage deployments with the Azure OpenAI Service.", "stackDetails": "", - "sourceUrl": "https://raw.githubusercontent.com/aamini7/postgres-rag-llm-demo/main/README.md", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/CreateAOAIDeployment/create-aoai-deployment.md", "documentationUrl": "", - "configurations": {} + "nextSteps": [], + "configurations": { + "permissions": [] + } }, { "status": "active", "key": "AksKaito/README.md", - "title": "Deploy an AI model on Azure Kubernetes Service (AKS) with the AI toolchain operator (preview)", - "description": "Learn how to enable the AI toolchain operator add-on on Azure Kubernetes Service (AKS) to simplify OSS AI model management and deployment.", + "title": "Deploy an AI model on AKS with the AI toolchain operator", + "description": "Learn how to enable the AI toolchain operator add-on on Azure Kubernetes Service (AKS) to simplify OSS AI model management and deployment", + "stackDetails": "", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/AksKaito/README.md", + "documentationUrl": "", + "nextSteps": [ + { + "title": "Check out the KAITO GitHub repository", + "url": "https://github.com/Azure/kaito" + } + ] + }, + { + "status": "inactive", + "key": "azure-aks-docs/articles/aks/trusted-access-feature.md", + "title": "Get secure resource access to Azure Kubernetes Service (AKS) using Trusted Access", + "description": "Learn how to use the Trusted Access feature to give Azure resources access to Azure Kubernetes Service (AKS) clusters.", "stackDetails": "", - "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/azure-aks-docs/refs/heads/main/articles/aks/ai-toolchain-operator.md", + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-aks-docs/articles/aks/trusted-access-feature.md", + "documentationUrl": "https://learn.microsoft.com/en-us/azure/aks/trusted-access-feature", + "nextSteps": [ + { + "title": "Deploy and manage cluster extensions for AKS", + "url": "https://learn.microsoft.com/en-us/azure/aks/cluster-extensions" + }, + { + "title": "Deploy the Azure Machine Learning extension on an AKS or Azure Arc–enabled Kubernetes cluster", + "url": "https://learn.microsoft.com/en-us/azure/machine-learning/how-to-deploy-kubernetes-extension" + }, + { + "title": "Deploy Azure Backup on an AKS cluster", + "url": "https://learn.microsoft.com/en-us/azure/backup/azure-kubernetes-service-backup-overview" + }, + { + "title": "Set agentless container posture in Microsoft Defender for Cloud for an AKS cluster", + "url": "https://learn.microsoft.com/en-us/azure/defender-for-cloud/concept-agentless-containers" + } + ], + "configurations": { + "permissions": [ + ], + "configurableParams": [ + { + "inputType": "textInput", + "commandKey": "RESOURCE_GROUP_NAME", + "title": "Resource Group Name", + "defaultValue": "" + }, + { + "inputType": "textInput", + "commandKey": "CLUSTER_NAME", + "title": "AKS Cluster Name", + "defaultValue": "" + } + ] + } + }, + { + "status": "inactive", + "key": "CreateLinuxVMSecureWebServer/create-linux-vm-secure-web-server.md", + "title": "Create a NGINX Webserver Secured via HTTPS", + "description": "This tutorial shows how to create a NGINX Webserver Secured via HTTPS.", + "stackDetails": [ + ], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/CreateLinuxVMSecureWebServer/create-linux-vm-secure-web-server.md", + "documentationUrl": "", + "nextSteps": [ + { + "title": "Learn about virtual machines", + "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/" + }, + { + "title": "Create and manage Linux VMs with the Azure CLI", + "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/linux/tutorial-manage-vm" + }, + { + "title": "Secure your Linux VM", + "url": "https://learn.microsoft.com/en-us/azure/virtual-machines/linux/tutorial-secure-vm" + } + ], + "configurations": { + } + }, + { + "status": "active", + "key": "azure-docs/articles/confidential-computing/confidential-enclave-nodes-aks-get-started.md", + "title": "Quickstart: Deploy an AKS cluster with confidential computing Intel SGX agent nodes by using the Azure CLI", + "description": "Learn how to create an Azure Kubernetes Service (AKS) cluster with enclave confidential containers a Hello World app by using the Azure CLI.", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-docs/articles/confidential-computing/confidential-enclave-nodes-aks-get-started.md", + "documentationUrl": "", + "nextSteps": [ + { + "title": "Samples to run Python, Node, and other applications through confidential containers", + "url": "https://github.com/Azure-Samples/confidential-container-samples" + }, + { + "title": "Enclave-aware Azure container samples in GitHub", + "url": "https://github.com/Azure-Samples/confidential-computing/blob/main/containersamples/" + } + ], + "configurations": { + "permissions": [] + } + }, + { + "status": "active", + "key": "azure-management-docs/articles/azure-linux/quickstart-azure-cli.md", + "title": "Quickstart: Deploy an Azure Linux Container Host for AKS cluster by using the Azure CLI", + "description": "Learn how to quickly create an Azure Linux Container Host for AKS cluster using the Azure CLI.", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-management-docs/articles/azure-linux/quickstart-azure-cli.md", "documentationUrl": "", - "configurations": {} + "nextSteps": [ + { + "title": "Azure Linux Container Host tutorial", + "url": "https://github.com/MicrosoftDocs/azure-management-docs/blob/main/articles/azure-linux/tutorial-azure-linux-create-cluster.md" + } + + ], + "configurations": { + "permissions": [] + } + }, + { + "status": "active", + "key": "azure-docs/articles/virtual-machine-scale-sets/tutorial-use-custom-image-cli.md", + "title": "Tutorial - Use a custom VM image in a scale set with Azure CLI", + "description": "Learn how to use the Azure CLI to create a custom VM image that you can use to deploy a Virtual Machine Scale Set", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-docs/articles/virtual-machine-scale-sets/tutorial-use-custom-image-cli.md", + "documentationUrl": "", + "nextSteps": [ + { + "title": "Deploy applications to your scale sets", + "url": "https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/tutorial-install-apps-cli" + } + + ], + "configurations": { + "permissions": [] + } + }, + { + "status": "active", + "key": "azure-docs/articles/virtual-network/create-virtual-machine-accelerated-networking.md", + "title": "Create an Azure Virtual Machine with Accelerated Networking", + "description": "Use Azure portal, Azure CLI, or PowerShell to create Linux or Windows virtual machines with Accelerated Networking enabled for improved network performance.", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-docs/articles/virtual-network/create-virtual-machine-accelerated-networking.md", + "documentationUrl": "https://learn.microsoft.com/en-us/azure/virtual-network/create-virtual-machine-accelerated-networking?tabs=cli", + "nextSteps": [ + { + "title": "How Accelerated Networking works in Linux and FreeBSD VMs", + "url": "https://learn.microsoft.com/en-us/azure/virtual-network/accelerated-networking-how-it-works" + } + + ], + "configurations": { + "permissions": [] + } + }, + { + "status": "inactive", + "key": "DeployHAPGOnAKSTerraform/deploy-ha-pg-on-aks-terraform.md", + "title": "Create a Highly Available PostgreSQL Cluster on Azure Kubernetes Service (AKS) using Terraform", + "description": "This tutorial shows how to create a Highly Available PostgreSQL cluster on AKS using the CloudNativePG operator", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/DeployHAPGOnAKSTerraform/deploy-ha-pg-on-aks-terraform.md", + "documentationUrl": "", + "nextSteps": [ + { + "title": "Create infrastructure for deploying a highly available PostgreSQL database on AKS", + "url": "https://learn.microsoft.com/en-us/azure/aks/create-postgresql-ha?tabs=helm" + }, + { + "title": "Deploy a highly available PostgreSQL database on AKS with Azure CLI", + "url": "https://learn.microsoft.com/en-us/azure/aks/deploy-postgresql-ha?tabs=helm" + } + + ], + "configurations": { + "permissions": [] + } + }, + { + "status": "active", + "key": "azure-aks-docs/articles/aks/workload-identity-migrate-from-pod-identity.md", + "title": "Migrate your Azure Kubernetes Service (AKS) pod to use workload identity", + "description": "In this Azure Kubernetes Service (AKS) article, you learn how to configure your Azure Kubernetes Service pod to authenticate with workload identity.", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/azure-aks-docs/articles/aks/workload-identity-migrate-from-pod-identity.md", + "documentationUrl": "", + "nextSteps": [ + { + "title": "Use Microsoft Entra Workload ID with Azure Kubernetes Service (AKS)", + "url": "https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview" + } + + ], + "configurations": { + "permissions": [], + "configurableParams": [ + { + "inputType": "textInput", + "commandKey": "MY_AKS_RESOURCE_GROUP", + "title": "Resource Group Name", + "defaultValue": "" + }, + { + "inputType": "textInput", + "commandKey": "MY_AKS_CLUSTER_NAME", + "title": "AKS Cluster Name", + "defaultValue": "" + } + ] + } + }, + { + "status": "inactive", + "key": "DeployCassandraOnAKS/deploy-cassandra-on-aks.md", + "title": "Deploy a Cassandra Cluster on AKS", + "description": "Learn how to deploy a Cassandra cluster on an Azure Kubernetes Service (AKS) cluster using Azure CLI and Kubernetes manifests.", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/DeployCassandraOnAKS/deploy-cassandra-on-aks.md", + "documentationUrl": "", + "nextSteps": [], + "configurations": { + "permissions": [] + } + }, + { + "status": "inactive", + "key": "DeployClickhouseOnAKS/deploy-clickhouse-on-aks.md", + "title": "Deploy ClickHouse Cluster on AKS", + "description": "Learn how to deploy a ClickHouse Cluster on Azure Kubernetes Service (AKS) using Azure CLI and Kubernetes manifests.", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/DeployClickhouseOnAKS/deploy-clickhouse-on-aks.md", + "documentationUrl": "", + "nextSteps": [], + "configurations": { + "permissions": [] + } + }, + { + "status": "inactive", + "key": "DeployLLMWithTorchserveOnAKS/deploy-llm-with-torchserve-on-aks.md", + "title": "Quickstart: Deploy a Large Language Model with TorchServe on Azure Kubernetes Service (AKS)", + "description": "Learn how to deploy a large language model using TorchServe on AKS.", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/DeployLLMWithTorchserveOnAKS/deploy-llm-with-torchserve-on-aks.md", + "documentationUrl": "", + "nextSteps": [], + "configurations": { + "permissions": [] + } + }, + { + "status": "active", + "key": "DeployTensorflowOnAKS/deploy-tensorflow-on-aks.md", + "title": "Setup: Deploy a Tensorflow Cluster on Azure Kubernetes Service (AKS)", + "description": "Learn how to deploy a Tensorflow cluster on Azure Kubernetes Service (AKS) using Azure CLI.", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/DeployTensorflowOnAKS/deploy-tensorflow-on-aks.md", + "documentationUrl": "", + "nextSteps": [], + "configurations": { + "permissions": [] + } + }, + { + "status": "inactive", + "key": "DeployTrinoOnAKS/deploy-trino-on-aks.md", + "title": "Deploy a Trino Cluster on Azure Kubernetes Service (AKS)", + "description": "Learn how to deploy a Trino Cluster on AKS using Azure CLI for scalable and distributed SQL query processing.", + "stackDetails": [], + "sourceUrl": "https://raw.githubusercontent.com/MicrosoftDocs/executable-docs/main/scenarios/DeployTrinoOnAKS/deploy-trino-on-aks.md", + "documentationUrl": "", + "nextSteps": [], + "configurations": { + "permissions": [] + } } ] diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 000000000..4b931a162 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,164 @@ +# ADA - AI Documentation Assistant + +Welcome to ADA! This tool helps you convert documents and troubleshoot errors efficiently using OpenAI's Large Language Models and the Azure Innovation Engine. + +## Features + +- Converts input documents using OpenAI's LLMs. +- Automatically installs required packages and the Innovation Engine. +- Runs tests on the converted document using the Innovation Engine. +- Provides detailed error logs and generates troubleshooting steps. +- Merges code blocks from the updated document with non-code content from the original document. +- Logs execution data to a CSV file for analytics. + +## Prerequisites + +- Python 3.6 or higher +- An Azure OpenAI API key +- Required Python packages: `openai`, `azure-identity`, `requests` + +## Installation + +1. Clone the repository: + ```bash + git clone + cd + ``` + +2. Install the required Python packages: + ```bash + pip install openai azure-identity requests + ``` + +3. Ensure you have the Azure OpenAI API key and endpoint set as environment variables: + ```bash + export AZURE_OPENAI_API_KEY= + export AZURE_OPENAI_ENDPOINT= + ``` + + To obtain an Azure OpenAI API key and endpoint, follow these steps: + + 1. **Sign in to the Azure Portal**: + - Navigate to [https://portal.azure.com](https://portal.azure.com) and log in with your Azure credentials. + + 2. **Create an Azure OpenAI Resource**: + - In the Azure Portal, select "Create a resource". + - Search for "Azure OpenAI" and select it from the results. + - Click "Create" to begin the setup process. + - Fill in the required details: + - **Subscription**: Choose your Azure subscription. + - **Resource Group**: Select an existing resource group or create a new one. + - **Region**: Choose the region closest to your location. + - **Name**: Provide a unique name for your OpenAI resource. + - **Pricing Tier**: Select the appropriate pricing tier (e.g., Standard S0). + - Click "Review + create" and then "Create" to deploy the resource. + + 3. **Deploy a Model in Azure AI Studio**: + - After creating your Azure OpenAI resource, navigate to the **Overview** page of your resource. + - Click on "Go to Azure AI Studio" to open the Azure AI Studio interface. + - In Azure AI Studio, select "Deployments" from the left-hand menu. + - Click "Deploy model" and choose `gpt-4o` from the Azure OpenAI collection. + - Provide a deployment name and configure any additional settings as needed. + - Click "Deploy" to deploy the model. + + 4. **Access Keys and Endpoint**: + - Once the deployment is complete, return to your Azure OpenAI resource in the Azure Portal. + - In the left-hand menu under "Resource Management", select "Keys and Endpoint". + - Here, you'll find your **Endpoint** URL and two **API keys** (`KEY1` and `KEY2`). + - Copy the endpoint URL and one of the API keys; you'll need them to authenticate your API calls. + + 5. **Set Environment Variables in Linux**: + - Open your terminal. + - Edit the `.bashrc` file using a text editor, such as `nano`: + ```bash + nano ~/.bashrc + ``` + - Add the following lines at the end of the file, replacing `` and `` with the values you obtained earlier: + ```bash + export AZURE_OPENAI_API_KEY="" + export AZURE_OPENAI_ENDPOINT="" + ``` + - Save and exit the editor (`Ctrl + X`, then `Y`, and `Enter` for nano). + - Apply the changes by sourcing the `.bashrc` file: + ```bash + source ~/.bashrc + ``` + - To verify that the environment variables are set correctly, you can use the `printenv` command: + ```bash + printenv | grep AZURE_OPENAI + ``` + This should display the variables you just set. + + By following these steps, you'll have your Azure OpenAI API key and endpoint configured, a model deployed, and your environment variables set up in a Linux environment, ready for integration into your applications. + + For a visual walkthrough of creating an Azure OpenAI resource and deploying a model, you might find the following video helpful: + +## Usage + +1. Run the script: + ```bash + python ada.py + ``` + +2. Enter the path to the input file or describe your intended workload when prompted. + +3. The script will process the file or description, convert it using OpenAI's GPT-4O model, and perform testing using the Innovation Engine. + +4. If the tests fail, the script will generate troubleshooting steps and attempt to correct the document. + +5. If the tests pass successfully, the script will merge code blocks from the updated document with non-code content from the original document. + +6. The final merged document will be saved, and a summary will be displayed. + +## Script Workflow + +1. **Initialization**: The script initializes the Azure OpenAI client and checks for required packages. + +2. **Input File or Workload Description**: Prompts the user to enter the path to the input file or describe their intended workload. + +3. **System Prompt**: Prepares the system prompt for the AI model. + +4. **File Content or Workload Description**: Reads the content of the input file or uses the provided workload description. + +5. **Install Innovation Engine**: Checks if the Innovation Engine is installed and installs it if necessary. + +6. **Conversion and Testing**: + - Attempts to convert the document using OpenAI's GPT-4O model. + - Runs tests on the converted document using the Innovation Engine. + - If tests fail, generates troubleshooting steps and attempts to correct the document. + +7. **Merge Documents**: + - If tests pass successfully, merges code blocks from the updated document with non-code content from the original document. + - Ensures that anything not within code blocks remains unchanged from the original document. + +8. **Remove Backticks**: Ensures that backticks are properly handled in the document. + +9. **Logging**: Logs execution data to `execution_log.csv`. + +10. **Final Output**: Saves the final merged document and provides the path. + +## Logging + +The script logs the following data to `execution_log.csv`: + +- Timestamp: The date and time when the script was run. +- Type: Whether the input was a file or a workload description. +- Input: The path to the input file or the workload description. +- Output: The path to the output file. +- Number of Attempts: The number of attempts made to generate a successful document. +- Errors Encountered: A summary of errors encountered during the process. +- Execution Time (in seconds): The total time taken to run the script. +- Success/Failure: Whether the script successfully generated a document without errors. + +## License + +This project is licensed under the MIT License - see the LICENSE file for details. + +## Contributing + +Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests. + +## Acknowledgments + +- [OpenAI](https://openai.com/) +- [Azure](https://azure.microsoft.com/) \ No newline at end of file diff --git a/tools/ada.py b/tools/ada.py new file mode 100644 index 000000000..d97c2b9d2 --- /dev/null +++ b/tools/ada.py @@ -0,0 +1,445 @@ +# WELCOME TO ADA - AI DOCUMENTATION ASSISTANT + +import os +import sys +import subprocess +import shutil +import pkg_resources +import csv +import time +from datetime import datetime +from openai import AzureOpenAI +from collections import defaultdict + +client = AzureOpenAI( + api_key=os.getenv("AZURE_OPENAI_API_KEY"), + api_version="2024-02-01", + azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT") +) + +deployment_name = 'gpt-4o' + +REQUIRED_PACKAGES = [ + 'openai', + 'azure-identity', + 'requests', +] + +for package in REQUIRED_PACKAGES: + try: + pkg_resources.get_distribution(package) + except pkg_resources.DistributionNotFound: + subprocess.check_call([sys.executable, "-m", "pip", "install", package]) + +system_prompt = """Exec Docs is a vehicle that transforms standard markdown into interactive, executable learning content, allowing code commands within the document to be run step-by-step or “one-click”. This is powered by the Innovation Engine, an open-source CLI tool that powers the execution and testing of these markdown scripts and can integrate with automated CI/CD pipelines. You are an Exec Doc writing expert. You will either write a new exec doc from scratch if no doc is attached or update an existing one if it is attached. You must adhere to the following rules while presenting your output: + +### Prerequisites + +Check if all prerequisites below are met before writing the Exec Doc. ***If any of the below prerequisites are not met, then either add them to the Exec Doc in progress or find another valid doc that can fulfill them. Do not move to the next step until then*** + +1. Ensure your Exec Doc is a markdown file. + + >**Note:** If you are converting an existing Azure Doc to an Exec Doc, you can either find it in your fork or copy the raw markdown content of the Azure Doc into a new markdown file in your local repo (this can be found by clicking "Raw" in the GitHub view of the Azure Doc). + +2. Ensure your Exec Doc is written with the LF line break type. + + **Example:** + + ![LF VSCode](https://github.com/MicrosoftDocs/executable-docs/assets/146123940/3501cd38-2aa9-4e98-a782-c44ae278fc21) + + >**Note:** The button will appear according to the IDE you are using. For the VS Code IDE, you can check this by clicking on the LF/CLRF button at the bottom right corner of the screen. + +3. Ensure all files that your Exec Doc references live under the same parent folder as your Exec Doc + + **Example:** + + If your Exec Doc ***my-exec-doc.md*** references a script file ***my-script.yaml*** within, the script file should be in the same folder as the Exec Doc. + + ```bash + ├── master-folder + │ └── parent-folder + │ ├── my-exec-doc.md + │ └── my-script.yaml + ``` + +4. Code blocks are used to provide examples, commands, or other code snippets in Exec Docs. They are distinguished by a triple backtick (```) at the start and end of the block. + + Ensure that the Exec Doc contains at least 1 code block and every input code block's type in the Exec Doc is taken from this list: + + - bash + - azurecli + - azure-cli-interactive + - azurecli-interactive + + **Example:** + + ```bash + az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION + ``` + + >**Note:** This rule does not apply to output code blocks, which are used to display the results of commands, scripts, or other operations. These blocks help in illustrating what the expected output should look like. They include, but are not limited to, the following types: _output, json, yaml, console, text, and log._ + + >**Note:** While Innovation Engine can _parse_ a code block of any type, given its current features, it can only _execute_ code blocks of the types above. So, it is important to ensure that the code blocks in your Exec Doc are of the types above. + +5. Headings are used to organize content in a document. The number of hashes indicates the level of the heading. For example, a single hash (#) denotes an h1 heading, two hashes (##) denote an h2 heading, and so on. Innovation Engine uses headings to structure the content of an Exec Doc and to provide a clear outline of the document's contents. + + Ensure there is at least one h1 heading in the Exec Doc, denoted by a single hash (#) at the start of the line. + + **Example:** + + ```markdown + # Quickstart: Deploy an Azure Kubernetes Service (AKS) cluster using Azure CLI + ``` + +### Writing Requirements + +6. Ensure that the Exec Doc does not include any commands or descriptions related to logging into Azure (e.g., `az login`) or setting the subscription ID. The user is expected to have already logged in to Azure and set their subscription beforehand. Do not include these commands or any descriptions about them in the Exec Doc. + +7. Ensure that the Exec Doc does not require any user interaction during its execution. The document should not include any commands or scripts that prompt the user for input or expect interaction with the terminal. All inputs must be predefined and handled automatically within the script. + +7. Appropriately add metadata at the start of the Exec Doc. Here are some mandatory fields: + + - title = the title of the Exec Doc + - description = the description of the Exec Doc + - ms.topic = what kind of a doc it is e.g. article, blog, etc. + - ms.date = the date the Exec Doc was last updated by author + - author = author's GitHub username + - ms.author = author's username (e.g. Microsoft Alias) + - **ms.custom = comma-separated list of tags to identify the Exec Doc (innovation-engine is the one tag that is mandatory in this list)** + + **Example:** + + ```yaml + --- + title: 'Quickstart: Deploy an Azure Kubernetes Service (AKS) cluster using Azure CLI' + description: Learn how to quickly deploy a Kubernetes cluster and deploy an application in Azure Kubernetes Service (AKS) using Azure CLI. + ms.topic: quickstart + ms.date: 11/11/2021 + author: namanparikh + ms.author: namanaprikh + ms.custom: devx-track-azurecli, mode-api, innovation-engine, linux-related-content + --- + ``` + +7. Ensure the environment variable names are not placeholders i.e. <> but have a certain generic, useful name. For the location/region parameter, default to "WestUS2" or "centralindia". Additionally, appropriately add descriptions below every section explaining what is happening in that section in crisp but necessary detail so that the user can learn as they go. + +8. Don't start and end your answer with ``` backticks!!! Don't add backticks to the metadata at the top!!!. + +8. Ensure that any info, literally any info whether it is a comment, tag, description, etc., which is not within a code block remains unchanged. Preserve ALL details of the doc. + +8. Environment variables are dynamic values that store configuration settings, system paths, and other information that can be accessed throughout a doc. By using environment variables, you can separate configuration details from the code, making it easier to manage and deploy applications in an environment like Exec Docs. + + Declare environment variables _as they are being used_ in the Exec Doc using the export command. This is a best practice to ensure that the variables are accessible throughout the doc. + + ### Example Exec Doc 1 - Environment variables declared at the _top_ of an Exec Doc, not declared as used + + **Environment Variables Section** + + We are at the start of the Exec Doc and are declaring environment variables that will be used throughout the doc. + + ```bash + export REGION="eastus" + ``` + + **Test Section** + + We are now in the middle of the Exec Doc and we will create a resource group. + + ```bash + az group create --name "MyResourceGroup" --location $REGION + ``` + + ### Example Exec Doc 2 - Environment Variables declared as used** + + **Test Section** + + We are in the middle of the Exec Doc and we will create a resource group. + + ```bash + export REGION="eastus" + export MY_RESOURCE_GROUP_NAME="MyResourceGroup" + az group create --name $MY_RESOURCE_GROUP_NAME --location $REGION + ``` + + >**Note:** If you are converting an existing Azure Doc to an Exec Doc and the Azure Doc does not environment variables at all, it is an Exec Doc writing best practice to add them. Additionally, if the Azure Doc has environment variables but they are not declared as they are being used, it is recommended to update them to follow this best practice. + + >**Note:** Don't have any spaces around the equal sign when declaring environment variables. + +9. A major component of Exec Docs is automated infrastructure deployment on the cloud. While testing the doc, if you do not update relevant environment variable names, the doc will fail when run/executed more than once as the resource group or other resources will already exist from the previous runs. + + Add a random suffix at the end of _relevant_ environment variable(s). The example below shows how this would work when you are creating a resource group. + + **Example:** + + ```bash + export RANDOM_SUFFIX=$(openssl rand -hex 3) + export REGION="eastus" + az group create --name "MyResourceGroup$RANDOM_SUFFIX" --location $REGION + ``` + + >**Note:** Add a random suffix to relevant variables that are likely to be unique for each deployment, such as resource group names, VM names, and other resources that need to be uniquely identifiable. However, do not add a random suffix to variables that are constant or environment-specific, such as region, username, or configuration settings that do not change between deployments. + + >**Note:** You can generate your own random suffix or use the one provided in the example above. The `openssl rand -hex 3` command generates a random 3-character hexadecimal string. This string is then appended to the resource group name to ensure that the resource group name is unique for each deployment. + +10. In Exec Docs, result blocks are distinguished by a custom expected_similarity comment tag followed by a code block. These result blocks indicate to Innovation Engine what the minimum degree of similarity should be between the actual and the expected output of a code block (one which returns something in the terminal that is relevant to benchmark against). Learn More: [Result Blocks](https://github.com/Azure/InnovationEngine/blob/main/README.md#result-blocks). + + Add result block(s) below code block(s) that you would want Innovation Engine to verify i.e. code block(s) which produce an output in the terminal that is relevant to benchmark against. Follow these steps when adding a result block below a code block for the first time: + + - Check if the code block does not already have a result block below it. If it does, ensure the result block is formatted correctly, as shown in the example below, and move to the next code block. + - [Open Azure Cloudshell](https://ms.portal.azure.com/#cloudshell/) + - **[Optional]**: Set your active subscription to the one you are using to test Exec Docs. Ideally, this sub should have permissions to run commands in your tested Exec Docs. Run the following command: + + ```bash + az account set --subscription "" + ``` + - Run the command in the code block in cloudshell. If it returns an output that you would want Innovation Engine to verify, copy the output from the terminal and paste it in a new code block below the original code block. The way a result code block should be formatted has been shown below, in this case for the command [az group create --name "MyResourceGroup123" --location eastus](http://_vscodecontentref_/1). + + **Example:** + ```markdown + Results: + + + + ```JSON + {{ + "id": "/subscriptions/abcabc-defdef-ghighi-jkljkl/resourceGroups/MyResourceGroup123", + "location": "eastus", + "managedBy": null, + "name": "MyResourceGroup123", + "properties": {{ + "provisioningState": "Succeeded" + }}, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" + }} + ``` + ``` + - If you run into an error while executing a code block or the code block is running in an infinite loop, update the Exec Doc based on the error stack trace, restart/clear Cloudshell, and rerun the command block(s) from the start until you reach that command block. This is done to override any potential issues that may have occurred during the initial run. More guidance is given in the [FAQ section](#frequently-asked-questions-faqs) below. + + >**Note:** The expected similarity value is a percentage of similarity between 0 and 1 which specifies how closely the true output needs to match the template output given in the results block - 0 being no similarity, 1 being an exact match. If you are uncertain about the value, it is recommended to set the expected similarity to 0.3 i.e. 30% expected similarity to account for small variations. Once you have run the command multiple times and are confident that the output is consistent, you can adjust the expected similarity value accordingly. + + >**Note:** If you are executing a command in Cloudshell which references a yaml/json file, you would need to create the yaml/json file in Cloudshell and then run the command. This is because Cloudshell does not support the execution of commands that reference local files. You can add the file via the cat command or by creating the file in the Cloudshell editor. + + >**Note:** Result blocks are not required but recommended for commands that return some output in the terminal. They help Innovation Engine verify the output of a command and act as checkpoints to ensure that the doc is moving in the right direction. + +11. Redacting PII from the output helps protect sensitive information from being inadvertently shared or exposed. This is crucial for maintaining privacy, complying with data protection regulations, and furthering the company's security posture. + + Ensure result block(s) have all the PII (Personally Identifiable Information) stricken out from them and replaced with x’s. + + **Example:** + + ```markdown + Results: + + + + ```JSON + {{ + "id": "/subscriptions/xxxxx-xxxxx-xxxxx-xxxxx/resourceGroups/MyResourceGroupxxx", + "location": "eastus", + "managedBy": null, + "name": "MyResourceGroupxxx", + "properties": {{ + "provisioningState": "Succeeded" + }}, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" + }} + ``` + ``` + + >**Note:** The number of x's used to redact PII need not be the same as the number of characters in the original PII. Furthermore, it is recommended not to redact the key names in the output, only the values containing the PII (which are usually strings). + + >**Note:** Here are some examples of PII in result blocks: Unique identifiers for resources, Email Addresses, Phone Numbers, IP Addresses, Credit Card Numbers, Social Security Numbers (SSNs), Usernames, Resource Names, Subscription IDs, Resource Group Names, Tenant IDs, Service Principal Names, Client IDs, Secrets and Keys. + +12. If you are converting an existing Azure Doc to an Exec Doc and if the existing doc contains a "Delete Resources" (or equivalent section) comprising resource/other deletion command(s), remove the code blocks in that section or remove that section entirely + + >**Note:** We remove commands from this section ***only*** in Exec Docs. This is because Innovation Engine executes all relevant command(s) that it encounters, inlcuding deleting the resources. That would be counterproductive to automated deployment of cloud infrastructure + +## WRITE AND ONLY GIVE THE EXEC DOC USING THE ABOVE RULES FOR THE FOLLOWING WORKLOAD: """ + +def install_innovation_engine(): + if shutil.which("ie") is not None: + print("\nInnovation Engine is already installed.\n") + return + print("\nInstalling Innovation Engine...\n") + subprocess.check_call( + ["curl", "-Lks", "https://raw.githubusercontent.com/Azure/InnovationEngine/v0.2.3/scripts/install_from_release.sh", "|", "/bin/bash", "-s", "--", "v0.2.3"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL + ) + print("\nInnovation Engine installed successfully.\n") + +def get_last_error_log(): + log_file = "ie.log" + if os.path.exists(log_file): + with open(log_file, "r") as f: + lines = f.readlines() + error_index = None + for i in range(len(lines) - 1, -1, -1): + if "level=error" in lines[i]: + error_index = i + break + if error_index is not None: + return "".join(lines[error_index:]) + return "No error log found." + +def remove_backticks_from_file(file_path): + with open(file_path, "r") as f: + lines = f.readlines() + + if lines and "```" in lines[0]: + lines = lines[1:] + + if lines and "```" in lines[-1]: + lines = lines[:-1] + + # Remove backticks before and after the metadata section + if lines and "---" in lines[0]: + for i in range(1, len(lines)): + if "---" in lines[i]: + if "```" in lines[i + 1]: + lines = lines[:i + 1] + lines[i + 2:] + break + + with open(file_path, "w") as f: + f.writelines(lines) + +def log_data_to_csv(data): + file_exists = os.path.isfile('execution_log.csv') + with open('execution_log.csv', 'a', newline='') as csvfile: + fieldnames = ['Timestamp', 'Type', 'Input', 'Output', 'Number of Attempts', 'Errors Encountered', 'Execution Time (in seconds)', 'Success/Failure'] + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + if not file_exists: + writer.writeheader() + writer.writerow(data) + +def main(): + print("\nWelcome to ADA - AI Documentation Assistant!\n") + print("This tool helps you write and troubleshoot Executable Documents efficiently!\n") + + user_input = input("Please enter the path to your markdown file for conversion or describe your intended workload: ") + + if os.path.isfile(user_input) and user_input.endswith('.md'): + input_type = 'file' + with open(user_input, "r") as f: + input_content = f.read() + else: + input_type = 'workload_description' + input_content = user_input + + install_innovation_engine() + + max_attempts = 11 + attempt = 1 + if input_type == 'file': + output_file = f"converted_{os.path.splitext(os.path.basename(user_input))[0]}.md" + else: + output_file = "generated_exec_doc.md" + + start_time = time.time() + errors_encountered = [] + + while attempt <= max_attempts: + if attempt == 1: + print(f"\n{'='*40}\nAttempt {attempt}: Generating Exec Doc...\n{'='*40}") + response = client.chat.completions.create( + model=deployment_name, + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": input_content} + ] + ) + output_file_content = response.choices[0].message.content + with open(output_file, "w") as f: + f.write(output_file_content) + else: + print(f"\n{'='*40}\nAttempt {attempt}: Generating corrections based on error...\n{'='*40}") + response = client.chat.completions.create( + model=deployment_name, + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": input_content}, + {"role": "assistant", "content": output_file_content}, + {"role": "user", "content": f"The following error(s) have occurred during testing:\n{errors_text}\nPlease carefully analyze these errors and make necessary corrections to the document to prevent them from happening again. Try to find different solutions if the same errors keep occurring. \nGiven that context, please think hard and don't hurry. I want you to correct the converted document in ALL instances where this error has been or can be found. Then, correct ALL other errors apart from this that you see in the doc. ONLY GIVE THE UPDATED DOC, NOTHING ELSE"} + ] + ) + output_file_content = response.choices[0].message.content + with open(output_file, "w") as f: + f.write(output_file_content) + + remove_backticks_from_file(output_file) + + print(f"\n{'-'*40}\nRunning Innovation Engine tests...\n{'-'*40}") + try: + result = subprocess.run(["ie", "test", output_file], capture_output=True, text=True, timeout=660) + except subprocess.TimeoutExpired: + print("The 'ie test' command timed out after 11 minutes.") + errors_encountered.append("The 'ie test' command timed out after 11 minutes.") + attempt += 1 + continue # Proceed to the next attempt + if result.returncode == 0: + print(f"\n{'*'*40}\nAll tests passed successfully.\n{'*'*40}") + success = True + print(f"\n{'='*40}\nProducing Exec Doc...\n{'='*40}") + if input_type == 'file': + response = client.chat.completions.create( + model=deployment_name, + messages=[ + f"The following errors have occurred during testing:\n{errors_text}\n{additional_instruction}\nPlease carefully analyze these errors and make necessary corrections to the document to prevent them from happening again. ONLY GIVE THE UPDATED DOC, NOTHING ELSE" + ] + ) + output_file_content = response.choices[0].message.content + with open(output_file, "w") as f: + f.write(output_file_content) + remove_backticks_from_file(output_file) + break + else: + print(f"\n{'!'*40}\nTests failed. Analyzing errors...\n{'!'*40}") + error_log = get_last_error_log() + errors_encountered.append(error_log.strip()) + errors_text = "\n\n ".join(errors_encountered) + # Process and count error messages + error_counts = defaultdict(int) + for error in errors_encountered: + lines = error.strip().split('\n') + for line in lines: + if 'Error' in line or 'Exception' in line: + error_counts[line] += 1 + + # Identify repeating errors + repeating_errors = {msg: count for msg, count in error_counts.items() if count > 1} + + # Prepare additional instruction if there are repeating errors + if repeating_errors: + repeating_errors_text = "\n".join([f"Error '{msg}' has occurred {count} times." for msg, count in repeating_errors.items()]) + additional_instruction = f"The following errors have occurred multiple times:\n{repeating_errors_text}\nPlease consider trying a different approach to fix these errors." + else: + additional_instruction = "" + print(f"\nError: {error_log.strip()}") + attempt += 1 + success = False + + if attempt > max_attempts: + print(f"\n{'#'*40}\nMaximum attempts reached without passing all tests.\n{'#'*40}") + + end_time = time.time() + execution_time = end_time - start_time + + log_data = { + 'Timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + 'Type': input_type, + 'Input': user_input, + 'Output': output_file, + 'Number of Attempts': attempt-1, + 'Errors Encountered': "\n\n ".join(errors_encountered), + 'Execution Time (in seconds)': execution_time, + 'Success/Failure': "Success" if success else "Failure" + } + + log_data_to_csv(log_data) + + print(f"\nThe updated file is stored at: {output_file}\n") + +if __name__ == "__main__": + main() diff --git a/tools/converted_test.md b/tools/converted_test.md new file mode 100644 index 000000000..b3a49e3ab --- /dev/null +++ b/tools/converted_test.md @@ -0,0 +1,248 @@ +--- +title: 'Tutorial: Create & manage a Virtual Machine Scale Set – Azure CLI' +description: Learn how to use the Azure CLI to create a Virtual Machine Scale Set, along with some common management tasks such as how to start and stop an instance, or change the scale set capacity. +author: ju-shim +ms.author: jushiman +ms.topic: tutorial +ms.service: azure-virtual-machine-scale-sets +ms.date: 10/05/2023 +ms.reviewer: mimckitt +ms.custom: mimckitt, devx-track-azurecli, innovation-engine +--- + +# Tutorial: Create and manage a Virtual Machine Scale Set with Azure CLI + +A Virtual Machine Scale Set allows you to deploy and manage a set of virtual machines. Throughout the lifecycle of a Virtual Machine Scale Set, you may need to run one or more management tasks. In this tutorial, you will learn how to: + +- Create a resource group. +- Create a Virtual Machine Scale Set. +- Scale out and in. +- Stop, start, and restart VM instances. + +> [!div class="checklist"] +> * Create a resource group. +> * Create a Virtual Machine Scale Set. +> * Scale out and in. +> * Stop, Start, and restart VM instances. + +This article requires Azure CLI version 2.0.29 or later. If using Azure Cloud Shell, the latest version is already installed. + +--- + +## Create a resource group + +An Azure resource group is a container that holds related resources. A resource group must be created before a Virtual Machine Scale Set. This example uses a unique random suffix for the resource group name to avoid conflicts. Replace `` with a unique value. + +```bash +export RANDOM_SUFFIX=$(openssl rand -hex 3) +export REGION="westus2" +export RESOURCE_GROUP_NAME="myResourceGroup$RANDOM_SUFFIX" + +az group create --name $RESOURCE_GROUP_NAME --location $REGION +``` + +The resource group name is used when you create or modify a scale set throughout this tutorial. + +Results: + + + +```json +{ + "id": "/subscriptions/xxxxx-xxxxx-xxxxx/resourceGroups/myResourceGroupxxx", + "location": "westus2", + "managedBy": null, + "name": "myResourceGroupxxx", + "properties": { + "provisioningState": "Succeeded" + }, + "tags": null, + "type": "Microsoft.Resources/resourceGroups" +} +``` + +--- + +## Create a Virtual Machine Scale Set + +> [!IMPORTANT] +> Starting November 2023, VM scale sets created using PowerShell and Azure CLI will default to Flexible Orchestration Mode if no orchestration mode is specified. For more information about this change and what actions you should take, go to [Breaking Change for VMSS PowerShell/CLI Customers - Microsoft Community Hub](https://techcommunity.microsoft.com/t5/azure-compute-blog/breaking-change-for-vmss-powershell-cli-customers/ba-p/3818295). + +A Virtual Machine Scale Set is created using the `az vmss create` command. Replace `` with a supported image such as `Ubuntu2204`. The VM SKU size is set to `Standard_B1s`. SSH keys are generated if they don’t exist. + +```bash +export SCALE_SET_NAME="myScaleSet$RANDOM_SUFFIX" +export ADMIN_USERNAME="azureuser" +export VALID_IMAGE="Ubuntu2204" # Use a valid image from the supported list + +az vmss create \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $SCALE_SET_NAME \ + --orchestration-mode flexible \ + --image $VALID_IMAGE \ + --vm-sku "Standard_B1s" \ + --admin-username $ADMIN_USERNAME \ + --generate-ssh-keys +``` + +It takes a few minutes to create and configure the scale set resources and VM instances. A load balancer is also created to distribute traffic. + +Verify the scale set creation: + +```bash +az vmss list --resource-group $RESOURCE_GROUP_NAME --output table +``` + +--- + +## View information about VM instances + +To view a list of VM instances in your scale set, use the `az vmss list-instances` command. Flexible orchestration mode assigns dynamically generated instance names. + +```bash +az vmss list-instances \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $SCALE_SET_NAME \ + --output table +``` + +Results (example): + + + +```text +InstanceId ResourceGroup VmId ProvisioningState Location +----------- ----------------------- ------------------------------------ ----------------- ---------- +1 myResourceGroupxxx e768fb62-0d58-4173-978d-1f564e4a925a Succeeded westus2 +0 myResourceGroupxxx 5a2b34bd-1123-abcd-abcd-1623e0caf234 Succeeded westus2 +``` + +To see additional information about a specific VM instance, use the `az vm show` command: + +```bash +export INSTANCE_NAME=$(az vmss list-instances --resource-group $RESOURCE_GROUP_NAME --name $SCALE_SET_NAME --query "[0].name" -o tsv) + +az vm show --resource-group $RESOURCE_GROUP_NAME --name $INSTANCE_NAME +``` + +--- + +## Change the capacity of a scale set + +By default, two VM instances are created in the scale set. To increase or decrease instances, use the `az vmss scale` command. For example, scale to 3 instances: + +```bash +az vmss scale \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $SCALE_SET_NAME \ + --new-capacity 3 +``` + +Verify the updated instance count: + +```bash +az vmss list-instances \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $SCALE_SET_NAME \ + --output table +``` + +Results: + + + +```text +InstanceId ResourceGroup VmId ProvisioningState Location +----------- ----------------------- ------------------------------------ ----------------- ---------- +2 myResourceGroupxxx 54f68ce0-f123-abcd-abcd-4e6820cabccd Succeeded westus2 +1 myResourceGroupxxx e768fb62-0d58-4173-978d-1f564e4a925a Succeeded westus2 +0 myResourceGroupxxx 5a2b34bd-1123-abcd-abcd-1623e0caf234 Succeeded westus2 +``` + +--- + +## Stop instances in a scale set + +To stop individual VMs in Flexible orchestration mode, retrieve their unique names: + +```bash +export INSTANCE_NAME=$(az vmss list-instances --resource-group $RESOURCE_GROUP_NAME --name $SCALE_SET_NAME --query "[0].name" -o tsv) + +az vm stop \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $INSTANCE_NAME +``` + +For all instances, use: + +```bash +az vmss stop --resource-group $RESOURCE_GROUP_NAME --name $SCALE_SET_NAME +``` + +--- + +## Start instances in a scale set + +To start individual stopped VMs, use: + +```bash +az vm start \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $INSTANCE_NAME +``` + +To start all instances: + +```bash +az vmss start \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $SCALE_SET_NAME +``` + +--- + +## Restart instances in a scale set + +Restart specific instances: + +```bash +az vm restart \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $INSTANCE_NAME +``` + +Or restart all instances: + +```bash +az vmss restart \ + --resource-group $RESOURCE_GROUP_NAME \ + --name $SCALE_SET_NAME +``` + +--- + +## Clean up resources + +When you delete a resource group, all associated resources are deleted: + +```bash +az group delete --name $RESOURCE_GROUP_NAME --no-wait --yes +``` + +--- + +## Next steps + +In this tutorial, you learned how to perform common Virtual Machine Scale Set management tasks with Azure CLI: + +> [!div class="checklist"] +> * Create a resource group. +> * Create a scale set. +> * View and use specific VM sizes. +> * Manually scale a scale set. +> * Perform common management tasks such as stopping, starting, and restarting instances. + +Advance to the next tutorial to learn how to connect to scale set instances: + +> [!div class="nextstepaction"] +> [Use data disks with scale sets](tutorial-connect-to-instances-cli.md) \ No newline at end of file diff --git a/tools/execution_log.csv b/tools/execution_log.csv new file mode 100644 index 000000000..e78532cad --- /dev/null +++ b/tools/execution_log.csv @@ -0,0 +1,117 @@ +Timestamp,Type,Input,Output,Number of Attempts,Errors Encountered,Execution Time (in seconds),Success/Failure +2024-12-18 16:38:44,file,test.md,converted_test.md,5,"time=2024-12-18T16:23:54-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 1. +Error: command exited with 'exit status 1' and the message 'ERROR: Invalid image ""UbuntuLTS"". Use a valid image URN, custom image name, custom image id, VHD blob URI, or pick an image from ['CentOS85Gen2', 'Debian11', 'OpenSuseLeap154Gen2', 'RHELRaw8LVMGen2', 'SuseSles15SP5', 'Ubuntu2204', 'Ubuntu2404', 'Ubuntu2404Pro', 'FlatcarLinuxFreeGen2', 'Win2022Datacenter', 'Win2022AzureEditionCore', 'Win2019Datacenter', 'Win2016Datacenter', 'Win2012R2Datacenter', 'Win2012Datacenter']. +See vm create -h for more information on specifying an image. +' +StdErr: ERROR: Invalid image ""UbuntuLTS"". Use a valid image URN, custom image name, custom image id, VHD blob URI, or pick an image from ['CentOS85Gen2', 'Debian11', 'OpenSuseLeap154Gen2', 'RHELRaw8LVMGen2', 'SuseSles15SP5', 'Ubuntu2204', 'Ubuntu2404', 'Ubuntu2404Pro', 'FlatcarLinuxFreeGen2', 'Win2022Datacenter', 'Win2022AzureEditionCore', 'Win2019Datacenter', 'Win2016Datacenter', 'Win2012R2Datacenter', 'Win2012Datacenter']. +See vm create -h for more information on specifying an image. + + time=2024-12-18T16:24:16-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 1. +Error: command exited with 'exit status 1' and the message 'ERROR: {""error"":{""code"":""InvalidTemplateDeployment"",""message"":""The template deployment 'vmss_deploy_lLnmw6ctN6MOCXrDgQzZnHguu6N4pbkU' is not valid according to the validation procedure. The tracking id is '7a48dd61-2d63-4c23-af7e-da420cc89516'. See inner errors for details."",""details"":[{""code"":""SkuNotAvailable"",""message"":""The requested VM size for resource 'Following SKUs have failed for Capacity Restrictions: Standard_DS1_v2' is currently not available in location 'eastus'. Please try another size or deploy to a different location or different zone. See https://aka.ms/azureskunotavailable for details.""}]}} +' +StdErr: ERROR: {""error"":{""code"":""InvalidTemplateDeployment"",""message"":""The template deployment 'vmss_deploy_lLnmw6ctN6MOCXrDgQzZnHguu6N4pbkU' is not valid according to the validation procedure. The tracking id is '7a48dd61-2d63-4c23-af7e-da420cc89516'. See inner errors for details."",""details"":[{""code"":""SkuNotAvailable"",""message"":""The requested VM size for resource 'Following SKUs have failed for Capacity Restrictions: Standard_DS1_v2' is currently not available in location 'eastus'. Please try another size or deploy to a different location or different zone. See https://aka.ms/azureskunotavailable for details.""}]}} + + time=2024-12-18T16:27:21-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 5. +Error: command exited with 'exit status 3' and the message 'ERROR: (ResourceNotFound) The Resource 'Microsoft.Compute/virtualMachines/myScaleSet_instance1' under resource group 'myResourceGroup05635e' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix +Code: ResourceNotFound +Message: The Resource 'Microsoft.Compute/virtualMachines/myScaleSet_instance1' under resource group 'myResourceGroup05635e' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix +' +StdErr: ERROR: (ResourceNotFound) The Resource 'Microsoft.Compute/virtualMachines/myScaleSet_instance1' under resource group 'myResourceGroup05635e' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix +Code: ResourceNotFound +Message: The Resource 'Microsoft.Compute/virtualMachines/myScaleSet_instance1' under resource group 'myResourceGroup05635e' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix + + time=2024-12-18T16:31:03-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 5. +Error: command exited with 'exit status 1' and the message 'ERROR: (OperationNotAllowed) Operation 'VMScaleSetVMs.Deallocate.POST' is not allowed on Virtual Machine Scale Set 'myScaleSete2e071'. +Code: OperationNotAllowed +Message: Operation 'VMScaleSetVMs.Deallocate.POST' is not allowed on Virtual Machine Scale Set 'myScaleSete2e071'. +' +StdErr: ERROR: (OperationNotAllowed) Operation 'VMScaleSetVMs.Deallocate.POST' is not allowed on Virtual Machine Scale Set 'myScaleSete2e071'. +Code: OperationNotAllowed +Message: Operation 'VMScaleSetVMs.Deallocate.POST' is not allowed on Virtual Machine Scale Set 'myScaleSete2e071'. + + time=2024-12-18T16:34:17-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 4. +Error: command exited with 'exit status 2' and the message 'ERROR: unrecognized arguments: --instance-id 0 + +Examples from AI knowledge base: +az vm stop --resource-group MyResourceGroup --name MyVm +Power off (stop) a running VM. + +az vm stop --resource-group MyResourceGroup --name MyVm --skip-shutdown +Power off a running VM without shutting down. + +https://docs.microsoft.com/en-US/cli/azure/vm#az_vm_stop +Read more about the command in reference docs +' +StdErr: ERROR: unrecognized arguments: --instance-id 0 + +Examples from AI knowledge base: +az vm stop --resource-group MyResourceGroup --name MyVm +Power off (stop) a running VM. + +az vm stop --resource-group MyResourceGroup --name MyVm --skip-shutdown +Power off a running VM without shutting down. + +https://docs.microsoft.com/en-US/cli/azure/vm#az_vm_stop +Read more about the command in reference docs",909.2479140758514,Success +2024-12-19 13:09:10,workload_description,i want to create a linux vm and ssh into it,generated_exec_doc.md,3,"time=2024-12-19T13:07:08-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 2. +Error: command exited with 'exit status 1' and the message 'ERROR: Invalid image ""UbuntuLTS"". Use a valid image URN, custom image name, custom image id, VHD blob URI, or pick an image from ['CentOS85Gen2', 'Debian11', 'OpenSuseLeap154Gen2', 'RHELRaw8LVMGen2', 'SuseSles15SP5', 'Ubuntu2204', 'Ubuntu2404', 'Ubuntu2404Pro', 'FlatcarLinuxFreeGen2', 'Win2022Datacenter', 'Win2022AzureEditionCore', 'Win2019Datacenter', 'Win2016Datacenter', 'Win2012R2Datacenter', 'Win2012Datacenter']. +See vm create -h for more information on specifying an image. +' +StdErr: ERROR: Invalid image ""UbuntuLTS"". Use a valid image URN, custom image name, custom image id, VHD blob URI, or pick an image from ['CentOS85Gen2', 'Debian11', 'OpenSuseLeap154Gen2', 'RHELRaw8LVMGen2', 'SuseSles15SP5', 'Ubuntu2204', 'Ubuntu2404', 'Ubuntu2404Pro', 'FlatcarLinuxFreeGen2', 'Win2022Datacenter', 'Win2022AzureEditionCore', 'Win2019Datacenter', 'Win2016Datacenter', 'Win2012R2Datacenter', 'Win2012Datacenter']. +See vm create -h for more information on specifying an image. + + time=2024-12-19T13:07:20-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 2. +Error: command exited with 'exit status 1' and the message 'ERROR: An RSA key file or key value must be supplied to SSH Key Value. You can use --generate-ssh-keys to let CLI generate one for you +' +StdErr: ERROR: An RSA key file or key value must be supplied to SSH Key Value. You can use --generate-ssh-keys to let CLI generate one for you + + time=2024-12-19T13:08:19-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 3. +Error: command exited with 'exit status 255' and the message 'Pseudo-terminal will not be allocated because stdin is not a terminal. +Host key verification failed. +' +StdErr: Pseudo-terminal will not be allocated because stdin is not a terminal. +Host key verification failed.",135.19094800949097,Success +2024-12-20 21:08:11,workload_description,Creation of Speech Services application on Azure,generated_exec_doc.md,11,"time=2024-12-20T21:04:49-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 3. +Error: unexpected end of JSON input +StdErr: + + time=2024-12-20T21:05:06-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 3. +Error: unexpected end of JSON input +StdErr: + + time=2024-12-20T21:05:23-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 2. +Error: invalid character 'K' looking for beginning of value +StdErr: + + time=2024-12-20T21:05:40-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 2. +Error: invalid character 'K' after top-level value +StdErr: + + time=2024-12-20T21:05:59-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 2. +Error: invalid character 'K' looking for beginning of value +StdErr: + + time=2024-12-20T21:06:19-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 3. +Error: invalid character 'K' looking for beginning of value +StdErr: + + time=2024-12-20T21:06:41-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 3. +Error: invalid character 'K' looking for beginning of value +StdErr: + + time=2024-12-20T21:07:05-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 3. +Error: invalid character 'K' looking for beginning of value +StdErr: + + time=2024-12-20T21:07:29-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 3. +Error: invalid character 'K' looking for beginning of value +StdErr: + + time=2024-12-20T21:07:49-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 3. +Error: invalid character 'K' looking for beginning of value +StdErr: + + time=2024-12-20T21:08:11-08:00 level=error msg=Error testing scenario: failed to execute code block 0 on step 3. +Error: invalid character 'K' looking for beginning of value +StdErr:",216.4925456047058,Failure +2025-01-25 18:47:18,workload_description,new.py,generated_exec_doc.md,0,,1.9009339809417725,Success diff --git a/tools/generated_exec_doc.md b/tools/generated_exec_doc.md new file mode 100644 index 000000000..4018bcab8 --- /dev/null +++ b/tools/generated_exec_doc.md @@ -0,0 +1,3 @@ +It seems you've requested a workload titled "new.py," but no content or details are provided for this workload. Please provide more details or specify the objective of the Exec Doc you'd like me to create (e.g., Do you want to deploy a specific resource on Azure, set up a CI/CD pipeline, work with a particular Azure service like Virtual Machines, Kubernetes, Databases, etc.?). + +Once you provide this information, I can create a fully detailed and functional Exec Doc adherent to the rules mentioned above. \ No newline at end of file diff --git a/tools/stdout.txt b/tools/stdout.txt new file mode 100644 index 000000000..01537152b --- /dev/null +++ b/tools/stdout.txt @@ -0,0 +1,20 @@ +AZ_BATCH_NODE_MOUNTS_DIR=/mnt/batch/tasks/fsmounts +AZ_BATCH_TASK_WORKING_DIR=/mnt/batch/tasks/workitems/myJob/job-1/myTask1/wd +AZ_BATCH_TASK_DIR=/mnt/batch/tasks/workitems/myJob/job-1/myTask1 +AZ_BATCH_NODE_SHARED_DIR=/mnt/batch/tasks/shared +AZ_BATCH_TASK_USER=_azbatch +AZ_BATCH_NODE_IS_DEDICATED=true +AZ_BATCH_NODE_STARTUP_DIR=/mnt/batch/tasks/startup +AZ_BATCH_JOB_ID=myJob +AZ_BATCH_NODE_STARTUP_WORKING_DIR=/mnt/batch/tasks/startup/wd +AZ_BATCH_TASK_ID=myTask1 +AZ_BATCH_ACCOUNT_NAME=batchaccountd980a9 +AZ_BATCH_RESERVED_EPHEMERAL_DISK_SPACE_BYTES=1000000000 +AZ_BATCH_NODE_ROOT_DIR=/mnt/batch/tasks +AZ_BATCH_POOL_ID=myPool +AZ_BATCH_RESERVED_DISK_SPACE_BYTES=1000000000 +AZ_BATCH_ACCOUNT_URL=https://batchaccountd980a9.eastus2.batch.azure.com/ +AZ_BATCH_NODE_ID=tvmps_38766d42b76cb3aeb30719a252fa0782d11ba04294b3f4c339ccb3f08dbdb2a4_d +AZ_BATCH_TASK_USER_IDENTITY=PoolNonAdmin +AZ_BATCH_OS_RESERVED_EPHEMERAL_DISK_SPACE_BYTES=1000000000 +AZ_BATCH_CERTIFICATES_DIR=/mnt/batch/tasks/workitems/myJob/job-1/myTask1/certs