From fd6083653272f25e7cbeea847f1b27b7ae09ad96 Mon Sep 17 00:00:00 2001 From: jack-lauristen_teradata Date: Sun, 19 Nov 2023 21:46:37 -0800 Subject: [PATCH] azure and aws updates - docker volume - azure standalone deploys --- deployments/aws/all-in-one.yaml | 13 +- deployments/aws/jupyter.yaml | 7 +- deployments/azure/bicep/jupyter.bicep | 314 +++++++---------------- deployments/azure/bicep/workspaces.bicep | 132 ++++++---- 4 files changed, 194 insertions(+), 272 deletions(-) diff --git a/deployments/aws/all-in-one.yaml b/deployments/aws/all-in-one.yaml index ca2596b..61f06b9 100644 --- a/deployments/aws/all-in-one.yaml +++ b/deployments/aws/all-in-one.yaml @@ -481,16 +481,18 @@ Resources: TimeoutStartSec=0 Restart=always RestartSec=2 - ExecStartPre=-/usr/bin/mkdir -p /etc/td/workspaces + ExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited + ExecStartPre=-/usr/bin/mkdir -p /etc/td/workspace ExecStartPre=-/usr/bin/docker exec %n stop || true ExecStartPre=-/usr/bin/docker rm %n || true ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-workspaces:${ WorkspacesVersion } ExecStart=/usr/bin/docker run \ -e accept_license=Y \ -e PLATFORM=aws \ - -v /etc/td/workspaces:/etc/td \ + -v /etc/td/workspace:/etc/td \ -p ${ WorkspacesHttpPort }:3000 \ -p ${ WorkspacesGrpcPort }:3282 \ + --network ai_unlimited \ --rm --name %n teradata/ai-unlimited-workspaces:${ WorkspacesVersion } workspaces serve -v [Install] @@ -519,15 +521,18 @@ Resources: TimeoutStartSec=0 Restart=always RestartSec=2 - ExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter + ExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited + ExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{userdata,ipython} ExecStartPre=-/usr/bin/docker exec %n stop || true ExecStartPre=-/usr/bin/docker rm %n || true ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-jupyter:${ JupyterVersion } ExecStart=/usr/bin/docker run \ -e accept_license=Y \ -e JUPYTER_TOKEN=${ JupyterToken } \ - -v /etc/td/jupyter:/home/jovyan/JupyterLabRoot/userdata \ + -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \ + -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \ -p ${ JupyterHttpPort }:8888 \ + --network ai_unlimited \ --rm --name %n teradata/ai-unlimited-jupyter:${ JupyterVersion } [Install] diff --git a/deployments/aws/jupyter.yaml b/deployments/aws/jupyter.yaml index 17f93db..c8b3aaf 100644 --- a/deployments/aws/jupyter.yaml +++ b/deployments/aws/jupyter.yaml @@ -468,15 +468,18 @@ Resources: TimeoutStartSec=0 Restart=always RestartSec=2 - ExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter + ExecStartPre=-/usr/bin/docker network create -d bridge ai_unlimited + ExecStartPre=-/usr/bin/mkdir -p /etc/td/jupyter/{userdata,ipython} ExecStartPre=-/usr/bin/docker exec %n stop || true ExecStartPre=-/usr/bin/docker rm %n || true ExecStartPre=/usr/bin/docker pull teradata/ai-unlimited-jupyter:${ JupyterVersion } ExecStart=/usr/bin/docker run \ -e accept_license=Y \ -e JUPYTER_TOKEN=${ JupyterToken } \ - -v /etc/td/jupyter:/home/jovyan/JupyterLabRoot/userdata \ + -v /etc/td/jupyter/userdata:/home/jovyan/JupyterLabRoot/userdata \ + -v /etc/td/jupyter/ipython:/home/jovyan/.ipython \ -p ${ JupyterHttpPort }:8888 \ + --network ai_unlimited \ --rm --name %n teradata/ai-unlimited-jupyter:${ JupyterVersion } [Install] diff --git a/deployments/azure/bicep/jupyter.bicep b/deployments/azure/bicep/jupyter.bicep index cc62ad2..2d68a5e 100644 --- a/deployments/azure/bicep/jupyter.bicep +++ b/deployments/azure/bicep/jupyter.bicep @@ -1,13 +1,14 @@ -@description('Name for the jupyter service virtual machine.') -param jupyterName string = 'jupyter' +targetScope = 'subscription' -@description('SSH public key value') -@secure() -param sshPublicKey string +@description('name for the resource group.') +param ResourceGroupName string = 'ai-unlimited-jupyter' -@description('jupyter token value') +@description('Name for the Jupyter Labs service\'s virtual machine.') +param JupyterName string + +@description('SSH public key value') @secure() -param jupyterToken string +param PublicKey string @description('The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version.') @allowed([ @@ -15,252 +16,121 @@ param jupyterToken string 'Ubuntu-2004' 'Ubuntu-2204' ]) -param ubuntuOSVersion string = 'Ubuntu-2004' +param OSVersion string = 'Ubuntu-2004' -@description('The size of the VM') -param vmSize string = 'Standard_D2s_v3' +@description('The Jupyter Labs VM type') +param InstanceType string = 'Standard_D2s_v3' -@description('ID of the subnet in the virtual network') -param networkName string +@description('Name of the network to run the Jupyter Labs service in') +param Network string -@description('ID of the subnet in the virtual network') -param subnetName string +@description('Name of the subnet to run the Jupyter Labs service in') +param Subnet string -@description('The CIDR ranges that can be used to communicate with the jupyter instance.') -param accessCidrs array = ['0.0.0.0/0'] +@description('Name of the network security group') +param SecurityGroup string = 'JupyterSecurityGroup' -@description('port to access the jupyter service UI.') -param httpPort string = '8888' +@description('The CIDR ranges that can be used to communicate with the Jupyter Labs service instance.') +param AccessCIDRs array = [ '0.0.0.0/0' ] -@description('allow access the jupyter ssh port from the access cidr.') -param sshAccess bool = true +@description('port to access the Jupyter Labs UI.') +param JupyterHttpPort string = '8888' -var imageReference = { - 'Ubuntu-1804': { - publisher: 'Canonical' - offer: 'UbuntuServer' - sku: '18_04-lts-gen2' - version: 'latest' - } - 'Ubuntu-2004': { - publisher: 'Canonical' - offer: '0001-com-ubuntu-server-focal' - sku: '20_04-lts-gen2' - version: 'latest' - } - 'Ubuntu-2204': { - publisher: 'Canonical' - offer: '0001-com-ubuntu-server-jammy' - sku: '22_04-lts-gen2' - version: 'latest' - } -} -var publicIPAddressName = '${jupyterName}PublicIP' -var networkInterfaceName = '${jupyterName}NetInt' -var networkSecurityGroupName = '${jupyterName}SecGroup' +@description('Source Application Security Groups to access the Jupyter Labs service api.') +param SourceAppSecGroups array = [] -var osDiskType = 'Standard_LRS' -var linuxConfiguration = { - disablePasswordAuthentication: true - ssh: { - publicKeys: [ - { - path: '/home/azureuser/.ssh/authorized_keys' - keyData: sshPublicKey - } - ] - } -} +@description('Destination Application Security Groups to give access to Jupyter Labs service instance.') +param detinationAppSecGroups array = [] + +@description('allow access the Jupyter Labs ssh port from the access cidr.') +param AllowPublicSSH bool = true + +@description('should we use a new or existing volume for persistent data on the workspace server.') +@allowed([ 'New', 'None', 'Existing' ]) +param UsePersistentVolume string = 'New' -var trustedExtensionName = 'GuestAttestation' -var trustedExtensionPublisher = 'Microsoft.Azure.Security.LinuxAttestation' -var trustedExtensionVersion = '1.0' -var trustedMaaTenantName = 'GuestAttestation' -var trustedMaaEndpoint = substring('emptystring', 0, 0) -var dockerExtensionName = 'DockerExtension' -var dockerExtensionPublisher = 'Microsoft.Azure.Extensions' -var dockerExtensionVersion = '1.1' +@description('size of the optional persistent disk to the workspace server.') +param PersistentVolumeSize int = 100 + +@description('Name of the existing persistent volume to attach. Must be in the same region and resourcegroup zone as the Jupyter Labs server.') +param ExistingPersistentVolume string = 'NONE' + +@description('Container Version of the Jupyter Labs service') +param JupyterVersion string = 'latest' + +@description('Join token for the Jupyter Labs service') +param JupyterToken string = uniqueString(subscription().id, utcNow()) var registry = 'teradata' -var repository = 'ai-unlimited-jupyter' -var version = 'latest' +var jupyterRepository = 'ai-unlimited-jupyter' + var cloudInitData = base64( format( - loadTextContent('templates/jupyter.cloudinit.yaml'), + loadTextContent('../templates/jupyter.cloudinit.yaml'), base64( format( - loadTextContent('templates/jupyter.service'), + loadTextContent('../templates/jupyter.service'), registry, - repository, - version, - httpPort, - jupyterToken + jupyterRepository, + JupyterVersion, + JupyterHttpPort, + JupyterToken ) ) ) ) +resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' existing = { + name: ResourceGroupName +} + resource network 'Microsoft.Network/virtualNetworks@2022-11-01' existing = { - name: networkName + scope: rg + name: Network } resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-11-01' existing = { parent: network - name: subnetName -} - -resource networkInterface 'Microsoft.Network/networkInterfaces@2022-11-01' = { - name: networkInterfaceName - location: resourceGroup().location - properties: { - ipConfigurations: [ - { - name: 'ipconfig1' - properties: { - subnet: { - id: subnet.id - } - privateIPAllocationMethod: 'Dynamic' - publicIPAddress: { - id: publicIPAddress.id - } - } - } - ] - networkSecurityGroup: { - id: networkSecurityGroup.id - } - } -} - -resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-11-01' = { - name: networkSecurityGroupName - location: resourceGroup().location - properties: { - securityRules: [ - { - name: 'SSH' - properties: { - priority: 700 - protocol: 'Tcp' - access: sshAccess ? 'Allow' : 'Deny' - direction: 'Inbound' - sourceAddressPrefixes: accessCidrs - sourcePortRange: '*' - destinationAddressPrefix: '*' - destinationPortRange: '22' - } - } - { - name: 'HTTP' - properties: { - priority: 701 - protocol: 'Tcp' - access: 'Allow' - direction: 'Inbound' - sourceAddressPrefixes: accessCidrs - sourcePortRange: '*' - destinationAddressPrefix: '*' - destinationPortRange: httpPort - } - } - ] - } -} - -resource publicIPAddress 'Microsoft.Network/publicIPAddresses@2022-11-01' = { - name: publicIPAddressName - location: resourceGroup().location - sku: { - name: 'Basic' - } - properties: { - publicIPAllocationMethod: 'Static' - publicIPAddressVersion: 'IPv4' - dnsSettings: { - domainNameLabel: uniqueString(resourceGroup().id, deployment().name, jupyterName) - } - idleTimeoutInMinutes: 4 - } -} - -resource vm 'Microsoft.Compute/virtualMachines@2023-03-01' = { - name: jupyterName - location: resourceGroup().location - identity: { - type: 'SystemAssigned' - } - properties: { - hardwareProfile: { - vmSize: vmSize - } - storageProfile: { - osDisk: { - createOption: 'FromImage' - managedDisk: { - storageAccountType: osDiskType - } - } - imageReference: imageReference[ubuntuOSVersion] - } - networkProfile: { - networkInterfaces: [ - { - id: networkInterface.id - } - ] - } - - osProfile: { - computerName: jupyterName - adminUsername: 'azureuser' - linuxConfiguration: linuxConfiguration - } - securityProfile: { - securityType: 'TrustedLaunch' - uefiSettings: { - secureBootEnabled: true - vTpmEnabled: true - } - } - userData: cloudInitData - } + name: Subnet } -resource jupyterName_extension_trusted 'Microsoft.Compute/virtualMachines/extensions@2023-03-01' = { - parent: vm - name: trustedExtensionName - location: resourceGroup().location - properties: { - publisher: trustedExtensionPublisher - type: trustedExtensionName - typeHandlerVersion: trustedExtensionVersion - autoUpgradeMinorVersion: true - settings: { - AttestationConfig: { - MaaSettings: { - maaEndpoint: trustedMaaEndpoint - maaTenantName: trustedMaaTenantName - } - } - } +module firewall '../modules/firewall.bicep' = { + scope: rg + name: 'firewall' + params: { + location: rg.location + name: SecurityGroup + accessCidrs: AccessCIDRs + sshAccess: AllowPublicSSH + jupyterHttpPort: JupyterHttpPort + sourceAppSecGroups: SourceAppSecGroups + detinationAppSecGroups: detinationAppSecGroups } } -resource jupyterName_extension_docker 'Microsoft.Compute/virtualMachines/extensions@2023-03-01' = { - parent: vm - name: dockerExtensionName - location: resourceGroup().location - properties: { - publisher: dockerExtensionPublisher - type: dockerExtensionName - typeHandlerVersion: dockerExtensionVersion - autoUpgradeMinorVersion: true +module workspaces '../modules/instance.bicep' = { + scope: rg + name: 'workspaces' + params: { + location: rg.location + name: JupyterName + adminUsername: 'azureuser' + sshPublicKey: PublicKey + dnsLabelPrefix: uniqueString(rg.id, deployment().name, JupyterName) + vmSize: InstanceType + subnetId: subnet.id + networkSecurityGroupID: firewall.outputs.Id + osVersion: OSVersion + cloudInitData: cloudInitData + usePersistentVolume: UsePersistentVolume + persistentVolumeSize: PersistentVolumeSize + existingPersistentVolume: ExistingPersistentVolume } } -output PublicIP string = publicIPAddress.properties.ipAddress -output PrivateIP string = networkInterface.properties.ipConfigurations[0].properties.privateIPAddress -output PublicHttpAccess string = 'http://${ publicIPAddress.properties.ipAddress }:${ httpPort }?token=${ jupyterToken }' -output PrivateHttpAccess string = 'http://${ networkInterface.properties.ipConfigurations[0].properties.privateIPAddress }:${ httpPort }' -output sshCommand string = 'ssh azureuser@${ publicIPAddress.properties.ipAddress }' +output PublicIP string = workspaces.outputs.PublicIP +output PrivateIP string = workspaces.outputs.PrivateIP +output JupyterLabPublicHttpAccess string = 'http://${workspaces.outputs.PublicIP}:${JupyterHttpPort}?token=${JupyterToken}' +output JupyterLabPrivateHttpAccess string = 'http://${workspaces.outputs.PrivateIP}:${JupyterHttpPort}?token=${JupyterToken}' +output sshCommand string = 'ssh azureuser@${workspaces.outputs.PublicIP}' +output SecurityGroup string = firewall.outputs.Id diff --git a/deployments/azure/bicep/workspaces.bicep b/deployments/azure/bicep/workspaces.bicep index ce82d59..509a966 100644 --- a/deployments/azure/bicep/workspaces.bicep +++ b/deployments/azure/bicep/workspaces.bicep @@ -1,14 +1,14 @@ targetScope = 'subscription' @description('name for the resource group.') -param resourceGroupName string = 'workspaces' +param ResourceGroupName string = 'ai-unlimited-workspace' -@description('Name for the workspaces service virtual machine.') -param workspacesName string +@description('Name for the Workspace service\'s virtual machine.') +param WorkspacesName string @description('SSH public key value') @secure() -param sshPublicKey string +param PublicKey string @description('The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version.') @allowed([ @@ -16,79 +16,123 @@ param sshPublicKey string 'Ubuntu-2004' 'Ubuntu-2204' ]) -param ubuntuOSVersion string = 'Ubuntu-2004' +param OSVersion string = 'Ubuntu-2004' -@description('The size of the VM') -param vmSize string = 'Standard_D2s_v3' +@description('The Workspace VM type') +param InstanceType string = 'Standard_D2s_v3' -@description('Name of the subnet to run Workspaces in') -param networkName string +@description('Name of the network to run the Workspace service in') +param Network string -@description('Name of the subnet to run Workspaces in') -param subnetName string +@description('Name of the subnet to run the Workspace service in') +param Subnet string -@description('Name of the Network Security Group') -param networkSecurityGroupName string = 'WorkspacesSecurityGroup' +@description('Name of the network security group') +param SecurityGroup string = 'WorkspacesSecurityGroup' -@description('The CIDR ranges that can be used to communicate with the workspaces instance.') -param accessCidrs array = ['0.0.0.0/0'] +@description('The CIDR ranges that can be used to communicate with the Workspace service instance.') +param AccessCIDRs array = [ '0.0.0.0/0' ] @description('port to access the workspaces service UI.') -param httpPort string = '3000' +param WorkspacesHttpPort string = '3000' @description('port to access the workspaces service api.') -param grpcPort string = '3282' +param WorkspacesGrpcPort string = '3282' + +@description('Source Application Security Groups to access the workspaces service api.') +param SourceAppSecGroups array = [] + +@description('Destination Application Security Groups to give access to workspaces service instance.') +param detinationAppSecGroups array = [] @description('GUID of the Workspaces Role') -param roleDefinitionId string +param RoleDefinitionId string @description('allow access the workspaces ssh port from the access cidr.') -param sshAccess bool = true - -var roleAssignmentName = guid(subscription().id, workspacesName, rg.id , roleDefinitionId) +param AllowPublicSSH bool = true + +@description('should we use a new or existing volume for persistent data on the workspace server.') +@allowed([ 'New', 'None', 'Existing' ]) +param UsePersistentVolume string = 'New' + +@description('size of the optional persistent disk to the workspace server.') +param PersistentVolumeSize int = 100 + +@description('Name of the existing persistent volume to attach. Must be in the same region and resourcegroup zone as the workspaces server.') +param ExistingPersistentVolume string = 'NONE' + +@description('Container Version of the Workspace service') +param WorkspacesVersion string = 'latest' + +var roleAssignmentName = guid(subscription().id, WorkspacesName, rg.id, RoleDefinitionId) + +var registry = 'teradata' +var workspaceRepository = 'ai-unlimited-workspaces' + +var cloudInitData = base64( + format( + loadTextContent('../templates/workspaces.cloudinit.yaml'), + base64( + format( + loadTextContent('../templates/workspaces.service'), + registry, + workspaceRepository, + WorkspacesVersion, + WorkspacesHttpPort, + WorkspacesGrpcPort, + subscription().subscriptionId, + subscription().tenantId + ) + ) + ) +) resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' existing = { - name: resourceGroupName + name: ResourceGroupName } resource network 'Microsoft.Network/virtualNetworks@2022-11-01' existing = { scope: rg - name: networkName + name: Network } resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-11-01' existing = { parent: network - name: subnetName + name: Subnet } -module firewall 'modules/firewall.bicep' = { +module firewall '../modules/firewall.bicep' = { scope: rg name: 'firewall' params: { location: rg.location - name: networkSecurityGroupName - accessCidrs: accessCidrs - sshAccess: sshAccess - httpPort: httpPort - grpcPort: grpcPort + name: SecurityGroup + accessCidrs: AccessCIDRs + sshAccess: AllowPublicSSH + workspacesHttpPort: WorkspacesHttpPort + workspacesGrpcPort: WorkspacesGrpcPort + sourceAppSecGroups: SourceAppSecGroups + detinationAppSecGroups: detinationAppSecGroups } } -module workspaces 'modules/instance.bicep' = { +module workspaces '../modules/instance.bicep' = { scope: rg name: 'workspaces' params: { location: rg.location - name: workspacesName + name: WorkspacesName adminUsername: 'azureuser' - sshPublicKey: sshPublicKey - dnsLabelPrefix: uniqueString(rg.id, deployment().name, workspacesName) - vmSize: vmSize + sshPublicKey: PublicKey + dnsLabelPrefix: uniqueString(rg.id, deployment().name, WorkspacesName) + vmSize: InstanceType subnetId: subnet.id networkSecurityGroupID: firewall.outputs.Id - httpPort: httpPort - grpcPort: grpcPort - ubuntuOSVersion: ubuntuOSVersion + osVersion: OSVersion + cloudInitData: cloudInitData + usePersistentVolume: UsePersistentVolume + persistentVolumeSize: PersistentVolumeSize + existingPersistentVolume: ExistingPersistentVolume } } @@ -96,16 +140,16 @@ resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { scope: subscription() name: roleAssignmentName properties: { - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', RoleDefinitionId) principalId: workspaces.outputs.PrincipleId } } output PublicIP string = workspaces.outputs.PublicIP output PrivateIP string = workspaces.outputs.PrivateIP -output PublicHttpAccess string = 'http://${ workspaces.outputs.PublicIP }:${ httpPort }' -output PrivateHttpAccess string = 'http://${ workspaces.outputs.PrivateIP }:${ httpPort }' -output PublicGrpcAccess string = 'http://${ workspaces.outputs.PublicIP }:${ grpcPort }' -output PrivateGrpcAccess string = 'http://${ workspaces.outputs.PrivateIP }:${ grpcPort }' -output SecurityGroup string = firewall.outputs.Id +output WorkspacesPublicHttpAccess string = 'http://${workspaces.outputs.PublicIP}:${WorkspacesHttpPort}' +output WorkspacesPrivateHttpAccess string = 'http://${workspaces.outputs.PrivateIP}:${WorkspacesHttpPort}' +output WorkspacesPublicGrpcAccess string = 'http://${workspaces.outputs.PublicIP}:${WorkspacesGrpcPort}' +output WorkspacesPrivateGrpcAccess string = 'http://${workspaces.outputs.PrivateIP}:${WorkspacesGrpcPort}' output sshCommand string = 'ssh azureuser@${workspaces.outputs.PublicIP}' +output SecurityGroup string = firewall.outputs.Id