-
Notifications
You must be signed in to change notification settings - Fork 5.7k
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
Optimise upload of deployment artifacts #8666
Comments
Thing to note, we need to remember about updating logic that checks if we should try to redeploy (https://github.com/serverless/serverless/blob/master/lib/plugins/aws/deploy/lib/checkForChanges.js#L156-L173) that currently relies on checking functions |
I can just confirm that AWS Cloudformation just cares about The question arises on how to perform the solution. Currently, I've seen that Solution proposal [1]: There is already a mechanism of checking if the deployment is required at all on Solution proposal [2]: probably more future-proof, however wider - as suggested by @medikoo, with the change how the artifacts are stored within the deployment bucket. The approach inspired from AWS SAM and |
@remi00 great thanks for that insight. It's great to have confirmed that AWS CF simply cares about I also believe that [2] is the way to go. I've also updated main description with solution proposal. It's not detailed implementation spec, but outlines what should change in internal logic to have that optimizations applied and working. We welcome a PR on that! If solution description is not good enough, I may attempt to prepare a more detailed implementation spec on how it should be tackled. |
I created a PoC based on serverless 2.51.2 that you can find 🚧 here, focusing on a configuration setup, which in overview includes the plugins:
There is a few findings that are worth addressing before making the mechanism production-ready. Undeterministic ZIP hashes
Dropping (optional)
Both issues above should be gone, see further comments. GIT variables plugin interfering When using Lambda versioning, this new deploy mechanism will interfere with And from @medikoo technical proposal questions:
Me and my organization would ❤️ to have this improvement within Serverless, so we are greatly interested in contribution to it. It'd be great if we get aligned with the concerns highlighted above first. I am fully open to discuss them and proceed with the work. 😎 |
I will also just add some stats for sample, large project that we tested before and after the improvements.
The largest benefit is therefore significant possible reduction of deployment process time. However, this is not the only one, others are:
|
I just realized that in config without serverless-webpack, ZIP artifacts created with pure serverless have last-modified timestamp zeroed (@pgrzesik change from ca. half a year ago) and serverless-webpack is going to address this issue the same way with PR serverless-heaven/serverless-webpack#911. Therefore, my main concerns will be gone and the solution will be much, much simpler, without changes to hash calculation. |
Thanks a lot for submitting your PoC @remi00 - I'm a bit busy at the moment but I will try to take a look at it tomorrow or next week (I'm not available on Friday). 🙇 |
Thanks @remi00 - I've reviewed the PoC and I think it looks like a good start. One thing that we definitely need to keep in mind as well is the functionality that removes previous deployment artifacts from here: https://github.com/serverless/serverless/blob/master/lib/plugins/aws/deploy/lib/cleanupS3Bucket.js How do you imagine supporting that with proposed changes? |
Please review my proposal:
I assume we need to keep backwards compatibility, so this area of implementation would be rather just expanded and aware of both legacy (current) and new approach. Proposal would be to adjust the way how all artifacts are stored, including storage of Current S3 storage structure: Code artifacts: <prefix>/<timestamp>-<datetime>/<service-name>.zip
Custom resources package: <prefix>/<timestamp>-<datetime>/custom-resources.zip
Template file: <prefix>/<timestamp>-<datetime>/compiled-cloudformation-template.json Proposed S3 storage structure: Code artifacts: <prefix>/<service-name>/<hash>.zip
Custom resources package: <prefix>/custom-resources/<hash>.zip
Template file: <prefix>/compiled-cloudformation-templates/<timestamp>-<datetime>.json Both current and new storage structures would be supported, so the analysis of the S3 bucket and compiled CFN template contents will allow to provide all currently available functionality for
Opt-in configuration flag for S3 storage structure Should the change of S3 storage structure be configurable? To some extent it's internal thing, but maybe some plugins or (rarely used features) still depend on it. Should we make it configurable then? @pgrzesik @medikoo Could you please advise on both matters? :) Thanks! |
Additional item is about layers: Updating layers storage management Should we reuse the flow for Lambda functions packaging and deploy also for the Lambda layers?
|
@remi00 great thanks for all the insight, and initial draft. It's all extremely valuable. You've already helped us answering some important questions we had. I've just updated top description with a proposal that also takes into account deployment versioning (so rollback handling etc.). I think it should answer already some of the questions you've had above. (@pgrzesik please also let me know what you think about updated proposal) Some additional points:
Let me know what you think @remi00 Additionally I think best if first we just focus on implementation spec, without writing any solution before we have full agreement on that. So we do not burn too much time on directions from which we will need to eventually revert. Concerning plugins, I suggest to take them into account, but if there's an issue with any, ideally if it's solved on plugin side, and not that we have to resign from most optimal path. |
@medikoo thanks for making this clarification, it's right on time :) For now I see no controversies about the technical proposals provided. I like the idea of start doing the things right away, technical proposals are precise enough. I already started doing updates including UTs updates. Hopefully I will be able to provide a number of (smaller) PRs instead of doing it big-bang. |
I've just realized that, as changes affect already packaging steps (we need to generate already a hash paths into CF template), we cannot really add intelligence of detecting whether it's a first time deployment and reading global state from S3 bucket (packaging step is by design offline operation). Hence I've updated description, so it doesn't cover that. It's a bit simpler now, but with a downside of leaving user either with deprecation notice or a requirement to add extra property to their service config until next major arrives. I'm not sure if we can do anything better, but any suggestions are highly welcome |
Thanks @medikoo - the updated proposal looks great in my opinion, I have only one concern.
This is a bit tricky, as switching to a new method will cause the old artifacts to stay in S3 forever as the new logic would not consider them at all during cleanup. One option is to either support them both for some time (which would probably mean indefinitely because we don't know when someone switches the packaging way) or introduce a detailed step-by-step instruction on how to migrate to new packaging/uploading approach. This has the downside of being harder to do and cause people to not migrate to new approach. |
I like the improvements as well. I actually planned to not use the global state in the first iteration anyway. I think that idea with per-deploy Based on that the concern from @pgrzesik about keeping the S3 dirty would be resolved - we will support both legacy and hash-based artifacts naming easily. And @medikoo - I do mean easily so no worries about too much blurry, or complex implementation. |
@pgrzesik for sake for simplicity I think approach should be that when switching to new approach we delete all old data, and not that it stays forever (I just added it to spec), so that it technically resets the history. Reasoning behind it is that trying to support both methods which theoretically may be switched numerous times (imagine versions 1, 2 on old method, 3,4,5 on new, then 6,7 on old and 8 on new etc.), will be difficult and will significantly raise the cost of implementation (and complex implementation is a perfect settlement for new bugs to be addressed) Additional reason is that I don't think it's a really crucial feature. I've just checked and our data shows that This usage data, actually raises the question on whether we should simply not drop the support for rollback, and make this as breaking change instead. Having that, changing the method wouldn't have to be breaking. |
That's definitely a valid approach, but doesn't it introduce an overhead for each deploy with this new packaging method, as we cannot be sure if there are no old artifacts left at any point in time, so we will have to check on each deploy?
I would not expect big usage of this feature, but I think it's quite important one, as you very rarely need to use it, but if you need to use it, it's really important to be working reliably. |
With each deploy we anyway need to scan S3 content. So I don't think there's any overhead here. (old method already scans and removes versions older than 10) |
Anyway, maybe my initial judgement that supporting both methods at once will require a complex implementation is not necessary valid. If that'll appear somewhat easy, then we could introduce it without introducing a deprecation and that's always preferable (although we still have plugins which may got broken, that'll have to be confirmed) |
@pgrzesik so the working implementation is available to review here: grasza-consulting#1 The highlight on how to handle deploy list, S3 cleanup after deploy and rollback: lookup into compiled cloudformation templates to determine what are the artifacts associated with the given deployment. |
@remi00 as I investigated there will be no changes needed to
@pgrzesik yes, exactly
It's purely for local processing (
There was similar idea in previous spec version, but we don't have to do that, we have all information we need in CF templates which are already uploaded |
Okay, in such case I assume that:
@pgrzesik @medikoo just plz confirm or clarify if necessary :) Thanks |
@remi00 I think you can also first create the cleanup ones, and then update the existing PR so it's up to date with master and reflects final implementation. No need to close it (unless you strongly prefer to close it, and start work over in new PR context, that's ok)
Exactly! Do you see any potential issues? If you have any other proposals let us know |
It makes perfect sense. I will proceed with making the updates. |
Could you clarify what the Also bumping up on performances:
That's the part I would like to clarify. Is there a worse performance (in some scenarios) because of the PR? |
@mnapoli this was part of a proposal that's no longer on a table.
No, there shouldn't be such case. I believe you're referring to some version that was explored by @remi00 at some point which is not considered at this point In top level description there's an updated full implementation proposal and currently we stick just to that |
@medikoo - shouldn't On the performance, those stats were based on legacy approach from initial PoC that involved significant changes to hash calculation to make them more deterministic. We now know it doesn't have to be made (it's thanks to changes ongoing into serverless-webpack plugin that my organization uses to build the ZIP files). Final approach does not involve these changes, so there is no performance degraded on the package step. |
@remi00 In my understanding this improvement can be added transparently, without a need for a flag. Do you see any potential issues of doing that? Note, that technically here we're fixing a design issue, which affected performance, I don't think that users should be bothered about that, we can simply just improve performance for them without a notice. |
@medikoo I think you are right. It's just about thinking of possible regression/incompatibilities with some custom configuration or plugins. Though I think I can't assess it as reliably as you can. So, after reconsidering it, if you think it's best to just introduce it as default behavior, that's good for me as well. |
I'm currently in process of investigating deeply all our plugin ecosystem, and there are many which override our packaging process injecting different artifact generation logic. Still they work in area we won't touch. Their end result is assigned to I've double checked whether there could be plugins that attempt to tweak our upload process, and just by using keywords |
Thanks a lot @medikoo! 👍 I proceed without any opt-in feature flag in the configuration. |
@medikoo One additional question for the changes regarding the structure within Asking this because I can imagine some edge cases (at worst case rather causing either that new mechanism won't work or it can lead to a slightly more messy S3 bucket) but on the other hand it may slightly increase the amount of changes. WDYT? |
I understand you refer to Now, when uploading, currently, we're taking the basename of artifact and upload it to With this optimization we will change it, so it's uploaded to |
I've just realized one thing when reviewing PR. Is that scenario where package artifacts are changed between package and deploy steps is already breaking with lambda versioning on. So technically we can consider such scenarios as invalid. In light of that there should be no issue in recalculating file hashes in deploy step, all we need is some confirmation in @remi00 what are your thoughts? |
I'm okay adding |
We need confirmation that service was packaged with Serverless Framework version that supports hash based naming. Do you have some other idea for it? Or use case it's supposed to address seems not clear? |
I just want to make sure I understand why we need this confirmation. Currently, Cloudformation template is being compiled on package step. So the destination S3 key paths are already as they should be, at any case (legacy or hash based naming). Upload happens on deploy step though. Maybe we shouldn't then recalculate the destination paths, but just rely on the template compiled on package step - use the S3 paths from there? |
Above approach would make deploy step much more agnostic of naming schemes (used and determined just on the package step). |
Indeed, that's a very good idea. I've also looked more closely how deploy resolves CF template to be uploaded to S3, and it's that in first phase of deploy state is restored from package path, and that ensures that internal So yes, we definitely can do that, and that I believe even more reduces needed changes, which is even better. I've updated the spec |
@medikoo As discussed within #9876, there is a challenge with resolving artifact S3 location paths from CF template, mostly with finding out proper name for custom resource artifact. Main driver for doing it this way was that it will be straightforward for regular Lambda functions and layers (for each such item we would just lookup into In case of custom resource lambda we do not have metadata like This destination S3 path lookup algorithm will be reconstructing, which is a smell (making it dependant on some other internals to reconstruct mapping between local paths and destination paths) - any future change to internals of how this compiled template is build may require reflecting changes in this algorithm as well. |
@remi00 I fully agree that this reconstruction logic will be leaky and prone to issues, and indeed it seems more optimal to go with Ideally if function which resolves hash for artifact automatically behind the scenes fills I've updated the spec, also I proposed where we could store retrieved hash map for convenient access when reading |
Jumping in from #10353, with the express intent not to hijack the conversation.... Since I didn't see the feature (which might be quite useful here, if I'm not mistaken about what you're trying to achieve), have you considered using S3 object If this is irrelevant, please ignore me, but I figured I would regret it if this turned out helpful and I didn't mention it. |
Use case description
Side related to #8499
Currently on service deployment, we generate and upload all artifacts to Serverless bucket, even if they remained unchanged against previous deployment.
It's highly inefficient, as in many cases it's upload and related resources updates that may take significant part of deploy process (although I haven't investigated on how AWS treats the case where same zip file (with same hash) is provided for lambda from different location (different uri), but I guess it's still treated as code update unconditionally).
While to maintain stateless nature (locally) we need to locally generate artifacts for all resources on each service deployment, having that done, we may then inspect against deployed ones whether their hash changed, and on that basis avoid unnecessary uploads.
Proposed solution
Note: This is based on implementation idea as presented in @remi00 PR, which seems to provide us with means to introduce this improvement transparently (without a need for additional flags or breaking changes). It additionally ensures that when relying on
sls package
andsls deploy --package
steps separately, we do not accidentally produce erroneous deployChange the location of where artifacts are stored in S3 bucket, to common folder where artifacts from all deployments are stored, and are named after their md5 hash. That will allow to easily confirm whether given artifact is already uploaded or not.
I propose to store them in
<deployment-prefix>/<service>/<stage>/code-artifacts
folder.In packaging step:
serverless-state.json
(so at deployment step we do not need to seclude generated hash names from generated CF template as that can be problematic)In deployment step:
serverless-state.json
file. For convenience ideally if given hash map is assigned toserverless.getProvider('aws).artifactsHashNamesMap
in context ofextendedValidate
where actualserverless-state.json
is read.code-artifacts
folder, but are not found in kept CF templates.The text was updated successfully, but these errors were encountered: