feat(toolkit): improve docker build time in CI#1776
Conversation
When running in CI, try to pull the latest image first and use it as cache for the build. CI is detected by the presence of the `CI` environment variable. Closes aws#1748
rix0rrr
left a comment
There was a problem hiding this comment.
In general, I like the change and the refactorings you made, thanks for that.
-
Can you describe a little more about the rationale behind the change? Are we not trading
docker buildtime fordocker pulltime? Are we guaranteed that pulling will be faster than building? Why limit this behavior change to CI builds only? -
Though I like the use of the
CIenvironment variable, I think there needs to be a command-line switch--ciwhich is threaded through to the build step. When not supplied, it could default to the presence of the environment variableCI. Be aware that--no-cishould also work to disable an autodetected CI build (it'll be the difference betweenundefinedandfalsein the yargs object).
packages/aws-cdk/lib/docker.ts
Outdated
| delete manifest.Created; | ||
|
|
||
| // Parent can change when using --cache-from in CI | ||
| delete manifest.Parent; |
There was a problem hiding this comment.
What does Parent represent here? Please tell me it has nothing to do with disk images
There was a problem hiding this comment.
It's the sha256 of the docker's parent image. When using --cache-from, the sha256 of the parent gets modified, even for the same final image.
There was a problem hiding this comment.
Wait, if it's a parent image, and the parent image changes on every build, then we cannot assume that the "current" image is the same either, can we?
There was a problem hiding this comment.
In this case the sha256 of the layers will change normally. I added this when I saw that for the same digest (on ECR) when using --cache-from, calculateImageFingerprint returned a different finger print, resulting in 3 tags on the same image (latest, fingerprint1 and fingerprint2). This needs further investigation. The optimal solution may be a mix between the custom finger print calculation and the docker digest. Looking into this.
There was a problem hiding this comment.
I'm removing this delete manifest.Parent, it's a mistake and not needed here. The sha256 of the Parent is also present in the Config.Image key so deleting it in one place only should not have had an effect.
packages/aws-cdk/lib/docker.ts
Outdated
| const qualifiedImageName = `${ecr.repositoryUri}:${tag}`; | ||
|
|
||
| await shell(['docker', 'tag', imageId, qualifiedImageName]); | ||
| await shell(['docker', 'tag', imageId, latest]); // Tag with `latest` also |
There was a problem hiding this comment.
You need to describe this change in the PR description as well.
We are indeed trading For very simple Dockerfiles (FROM + COPY/ADD, few layers) or for a complete change of the Dockerfile, I can imagine that it could be faster to build.
When running locally chances are that we already have a lot of layer cache available. In this case, pulling will almost always be slower than building.
OK, I will look into this. |
|
Thanks for the explanation. I'd like to see that rationale captured in one or two sentenced in a code comment somewhere. |
packages/aws-cdk/bin/cdk.ts
Outdated
| .command('deploy [STACKS..]', 'Deploys the stack(s) named STACKS into your AWS account', yargs => yargs | ||
| .option('exclusively', { type: 'boolean', alias: 'e', desc: 'only deploy requested stacks, don\'t include dependencies' }) | ||
| .option('require-approval', { type: 'string', choices: [RequireApproval.Never, RequireApproval.AnyChange, RequireApproval.Broadening], desc: 'what security-sensitive changes need manual approval' })) | ||
| .option('ci', { type: 'boolean', desc: 'Force CI detection. Use --no-ci to disable CI autodetection.', default: undefined }) |
There was a problem hiding this comment.
@rix0rrr The description makes no reference to docker/asset preparation. I thought that this option could be used elsewhere in the future. What about the documentation for this new option? I see that https://github.com/awsdocs/aws-cdk-user-guide/blob/master/doc_source/tools.md is not up to date...
There was a problem hiding this comment.
That seems good. @Doug-AWS will update the tools section once we release.
Please follow this by something like:
if (argv.ci === undefined) {
argv.ci = process.env.CI !== undefined;
}
To make sure that afterwards argv.ci is well-defined. Saves having to redo the CI-autodetection logic in every place where it's used. Configuring:
default: process.env.CI !== undefined
To force the default value to either true or false might also work.
There was a problem hiding this comment.
Tools is updated. I should have a new public build of the guide by EOW.
packages/aws-cdk/bin/cdk.ts
Outdated
| .command('deploy [STACKS..]', 'Deploys the stack(s) named STACKS into your AWS account', yargs => yargs | ||
| .option('exclusively', { type: 'boolean', alias: 'e', desc: 'only deploy requested stacks, don\'t include dependencies' }) | ||
| .option('require-approval', { type: 'string', choices: [RequireApproval.Never, RequireApproval.AnyChange, RequireApproval.Broadening], desc: 'what security-sensitive changes need manual approval' })) | ||
| .option('ci', { type: 'boolean', desc: 'Force CI detection. Use --no-ci to disable CI autodetection.', default: undefined }) |
There was a problem hiding this comment.
That seems good. @Doug-AWS will update the tools section once we release.
Please follow this by something like:
if (argv.ci === undefined) {
argv.ci = process.env.CI !== undefined;
}
To make sure that afterwards argv.ci is well-defined. Saves having to redo the CI-autodetection logic in every place where it's used. Configuring:
default: process.env.CI !== undefined
To force the default value to either true or false might also work.
packages/aws-cdk/lib/docker.ts
Outdated
| let loggedIn = false; | ||
|
|
||
| // In CI we try to pull latest first | ||
| if (ci === true || (process.env.CI && ci !== false)) { |
There was a problem hiding this comment.
Don't do the CI autodetection here, move it up to cdk.ts.
There was a problem hiding this comment.
Yep. I have a script that recreates the 'cdk --help' part of the tools section whenever we have a new release.
packages/aws-cdk/lib/docker.ts
Outdated
| 'build', | ||
| '--quiet', | ||
| asset.path]; | ||
| const command = process.env.CI |
There was a problem hiding this comment.
Need to use the variable here.
When running in CI, try to pull the latest image first and use it as cache for the build. CI is detected by the presence of the `CI` environment variable. Closes #1748
When running in CI, try to pull the latest image first and use it as cache for the build. CI is detected by the presence of the
CIenvironment variable or the--cicommand line option.When pushing docker images, always tag with
latest.Closes #1748
To be discussed: name of env var for detection,
CIis set by lots of CI providers (not by CodeBuild though), use a opt-in mechanism instead?Test (with empty ECR):
Pull Request Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license.