-
Notifications
You must be signed in to change notification settings - Fork 21
Add support for packed modules to keep deployments <250mb #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add support for packed modules to keep deployments <250mb #38
Conversation
|
N.B.: Marking this as draft while I run some tests locally - definitely open to feedback/review while I'm doing this, however. |
…ll-runtime into sean-r-williams/37_compressed-modules
|
Ran some tests in our account with @austoonz @afroz429, let me know what your thoughts are on this. |
austoonz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do like this concept! Super useful.
Comments are somewhat focused primarily from a performance aspect of reducing touch points to the file system.
powershell-runtime/source/bootstrap
Outdated
| # Unpack compressed modules, if present | ||
|
|
||
| # Combined | ||
| If (Test-RuntimePackedModule -Combined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For performance reasons (reading from disk is relatively slow), it would be great to merge these into a single call, that looks for both .zip and .nupkg files, and passes the files found into appropriate import functions.
As it stands, this will traverse the file system twice (in each of the Test-RuntimePackedModule calls, and then if something is found, again in the relative Import-* functions).
Perhaps the Test-RuntimePackedModule could return the specific paths to the files found, and the Import-* functions could accept a string of path values as a parameters reducing the disk reads.
| $BaseDirectories = @( | ||
| "/opt", | ||
| $Env:LAMBDA_TASK_ROOT | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this was call in bootstrap after the Set-PSModulePath call, perhaps this could use the paths configured in $env:PSModulePath for the evaluation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this is called before Set-PSModulePath (and dirs Set-PSModulePath looks for are only populated because this cmdlet says so), I don't think relying on contents of $env:PSModulePath would work as expected.
I could potentially put these paths in a separate $Script: scope variable, but given the very specific use case I'm not sure they'd be of any use.
powershell-runtime/source/modules/Private/Import-ModuleArchive.ps1
Outdated
Show resolved
Hide resolved
| ) | ||
|
|
||
| If ($SearchPaths | ? { Test-Path $_ }) { | ||
| $UnpackDirectory = '/tmp/powershell-custom-runtime-unpacked-modules/combined/' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be best to define in pwsh-runtime.psm1 as a script (/module) scoped variable. That will mean it can also be used to configure $env:PSModulePath in Set-PSModulePath, allowing the unpacked modules to be imported as normal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
powershell-runtime/source/modules/Private/Import-ModuleArchive.ps1
Outdated
Show resolved
Hide resolved
| $PackageDirectory = Split-Path $_.Value -Parent | ||
| if ($env:POWERSHELL_RUNTIME_VERBOSE -eq 'TRUE') { Write-Host "[RUNTIME-Import-ModulePackage]Importing module packages from $PackageDirectory" } | ||
| $RepositoryName = "Lambda-Local-$($_.Key)" | ||
| if ($env:POWERSHELL_RUNTIME_VERBOSE -eq 'TRUE') { Write-Host "[RUNTIME-Import-ModulePackage]Registering local package repository $RepositoryName" } | ||
| Register-PSResourceRepository -Name $RepositoryName -Uri $PackageDirectory -Trusted -Priority 1 | ||
| if ($env:POWERSHELL_RUNTIME_VERBOSE -eq 'TRUE') { Write-Host "[RUNTIME-Import-ModulePackage]Enumerating packages in $PackageDirectory (PSResource repository $RepositoryName)" } | ||
| Find-PSResource -Name * -Repository $RepositoryName | ForEach-Object -Parallel { | ||
| if ($env:POWERSHELL_RUNTIME_VERBOSE -eq 'TRUE') { Write-Host "[RUNTIME-Import-ModulePackage]Saving package $($_.Name) version $($_.Version) (PSResource repository $($using:RepositoryName))" } | ||
| $_ | Save-PSResource -SkipDependencyCheck -Path $using:PackageDirectory -Quiet -AcceptLicense -Confirm:$false | ||
| } | ||
| if ($env:POWERSHELL_RUNTIME_VERBOSE -eq 'TRUE') { Write-Host "[RUNTIME-Import-ModulePackage]Registering local package repository $RepositoryName" } | ||
| Unregister-PSResourceRepository -Name $RepositoryName -Confirm:$false | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these need to be invoked for every item from $SearchPaths.GetEnumerator()?
Instead, perhaps each file should be stored appropriately, and then a single repository is registered and used to save the modules.
$using:PackageDirectory - from another comment, this could use a module scoped variable for where these compressed/packed modules will be extracted into.
| '/opt/modules', # User supplied modules as part of Lambda Layers | ||
| [System.IO.Path]::Combine($env:LAMBDA_TASK_ROOT, 'modules') # User supplied modules as part of function package | ||
| ) -join ':' | ||
| If (Test-RuntimePackedModule -Combined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the paths to the extracted modules was the module scoped variable, we could just add that to the $env:ModulePath directly (perhaps if it exists, or just add it). rather than traversing the file system again.
…r `Set-PSModulePath` to use it
…eration of files/directories
Issue #, if available:
Related to #37.
Description of changes:
This PR adds support for customers to deploy compressed archives of modules, then unpack them at runtime. This provides a compelling option for customers with large sets of dependent modules, who would otherwise be constrained to 250MB minus the size of the PowerShell runtime (currently ~172 MB runtime size --> 78 MB dependency size).
Two forms of compressed dependencies are supported:
modules.zip. The contents of this archive are unpacked to a subdirectory of /tmp as-is. This format assumes someone's zipping a folder in$env:PSModulePath(or somewhere they saved several modules into) and trades layer support/reproducability for better compression ratios..nupkgfiles, in a subdirectory namedmodule-nupkgs. These packages are "saved" (installed) to a subdirectory of/tmpvia PSResourceGet. This should broadly align with the ideas behind Lambda's layer architecture, and allows for direct package usage (as a binary, without unpacking/etc.) off a given NuGet feed.Both forms of dependencies:
/opt/(for layers) or$env:LAMBDA_TASK_ROOT(for function packages).By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.