How to specify image name in sam build with template containing multiple functions
I've had a look through the extensive documentation for AWS SAM and which CLI parameters are supported for the sam build command but I couldn't see any option to specify the name of the docker image that gets built? I am already using the Metadata section with the DockerTag option to specify the tag but I'd like to specify the image name.
I have two Functions defined which are built as a single Docker image which builds and runs perfectly fine, however, when I run a sam build it seems to just select the Logical Resource ID of the first AWS::Serverless::Function defined in the template.
Here is my template:
Transform: AWS::Serverless-2016-10-31
Parameters:
DockerTag:
Type: String
Default: local-dev
Resources:
TestFunction1:
Type: AWS::Serverless::Function
Properties:
FunctionName: TestFunction1
PackageType: Image
ImageUri: !Sub 'testfunction1:${DockerTag}'
ImageConfig:
Command:
- TestLambdas::TestLambdas.TestFunction1::Invoke
Role: !Sub 'arn:aws:iam::${AWS::AccountId}:role/lambda-execution-role'
MemorySize: 256
Timeout: 30
Events:
ScheduledEvent:
Type: Schedule
Properties:
Schedule: rate(3 minutes)
Name: test-schedule-rule
Enabled: true
Metadata:
DockerTag: !Ref DockerTag
DockerContext: .
Dockerfile: ./Dockerfile
TestFunction2:
Type: AWS::Serverless::Function
Properties:
FunctionName: TestFunction2
PackageType: Image
ImageUri: !Sub 'testfunction1:${DockerTag}'
ImageConfig:
Command:
- TestLambdas::TestLambdas.TestFunction2::Invoke
Role: !Sub 'arn:aws:iam::${AWS::AccountId}:role/lambda-execution-role'
MemorySize: 256
Timeout: 30
Events:
ScheduledEvent:
Type: Schedule
Properties:
Schedule: rate(3 minutes)
Name: test-schedule-rule
Enabled: true
Metadata:
DockerTag: !Ref DockerTag
DockerContext: .
Dockerfile: ./Dockerfile
And here is the output of running sam build (Notice this one image being built contains two functions)
PS C:\Workspace\Lambda.Handlers\Lambda.Handlers.Test> sam build
Building codeuri: C:\Workspace\Lambda.Handlers\Lambda.Handlers.Test runtime: None metadata: {'DockerTag': 'local-dev', 'DockerContext': 'C:\\Workspace\\Lambda.Handlers\\Lambda.Handlers.Test', 'Dockerfile': './Dockerfile'} functions: ['TestFunction1', 'TestFunction2']
Building image for TestFunction1 function
Setting DockerBuildArgs: {} for TestFunction1 function
Step 1/3 : FROM public.ecr.aws/lambda/dotnet:5.0
---> 41fed005ba97
Step 2/3 : COPY ./* /var/task/
---> Using cache
---> 6fac8005ce03
Step 3/3 : ENTRYPOINT ["/lambda-entrypoint.sh"]
---> Using cache
---> d691b293f1c1
Successfully built d691b293f1c1
Successfully tagged testfunction1:local-dev <--------- I WOULD LIKE TO SET testfunction1 TO SOMETHING ELSE
Build Succeeded
Built Artifacts : .aws-sam\build
Built Template : .aws-sam\build\template.yaml
Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided
If I re-order TestFunction1 and TestFunction2, then the built image is then named testfunction2.
Looking forward to any help/advice if what I'm trying to achieve is even possible...
Thanks
I'd like to understand a bit more about what you're trying to do - are you looking to change the name of the image during the build?
The reason you're getting the testfunction1 name for both is because both functions have the same CodeUri, so we build it only once and share the image across both functions, which is intended behavior.
Are you trying to find a way for sam build to change the name of your input image into CodeUri into another image name, to do something like customimagename1:local-dev?
Hi @awood45 ,
Thanks for the response. You've mentioned CodeUri a couple of times, I assume you mean ImageUri in my templates case?
In regards to your first comment, I just tested then, and even if I change the ImageUri to be unique, i.e.
TestFunction1hasImageUri: !Sub 'testfunction1:${DockerTag}'andTestFunction2hasImageUri: !Sub 'testfunction2:${DockerTag}'
Then still only one image is built, and its still called testfunction1:local-dev.
In regards to your second comment, its not so much that I'd like to override the ImageUri since I know I can do that using a parameter at runtime (which I will). It is the second part of this second comment which I want to achieve, exactly: customimagename1:local-dev
Interestingly, I decided to go and take a look at the built SAM template in the .aws-sam\build directory.
The sam build has actually substituted both ImageUri values as testfunction1:local-dev so it has completely ignored the values I put in the ImageUri property in my template.
So is sam build smart enough to detect if some combination of image build parameters are the same, it recognises it would be the same image and then doesn't build it again? What those parameters are though, I'm not sure ??
Transform: AWS::Serverless-2016-10-31
Parameters:
DockerTag:
Type: String
Default: local-dev
Resources:
TestFunction1:
Type: AWS::Serverless::Function
Properties:
FunctionName: TestFunction1
PackageType: Image
ImageUri: testfunction1:local-dev
ImageConfig:
Command:
- TestLambdas::TestLambdas.TestFunction1::Invoke
Role:
Fn::Sub: arn:aws:iam::${AWS::AccountId}:role/lambda-execution-role
MemorySize: 256
Timeout: 30
Events:
ScheduledEvent:
Type: Schedule
Properties:
Schedule: rate(3 minutes)
Name: test-schedule-rule
Enabled: true
Metadata:
DockerTag:
Ref DockerTag
DockerContext: .
Dockerfile: ./Dockerfile
TestFunction2:
Type: AWS::Serverless::Function
Properties:
FunctionName: TestFunction2
PackageType: Image
ImageUri: testfunction1:local-dev
ImageConfig:
Command:
- TestLambdas::TestLambdas.TestFunction2::Invoke
Role:
Fn::Sub: arn:aws:iam::${AWS::AccountId}:role/lambda-execution-role
MemorySize: 256
Timeout: 30
Events:
ScheduledEvent:
Type: Schedule
Properties:
Schedule: rate(3 minutes)
Name: test-schedule-rule
Enabled: true
Metadata:
DockerTag:
Ref DockerTag
DockerContext: .
Dockerfile: ./Dockerfile
@mr-davidc I see that both functions reference the same tag under DockerTag, what happens if you reference different tags for the images?
I did as you requested @sriram-mv and it did indeed go through the process of building two images which are of course the same since the DockerContext and Dockerfile are identical.
I made the following changes to my template:
- TestFunction1 has
DockerTag: !Ref DockerTagand - TestFunction2 has
DockerTag: test-tag
Here is the sam build output:
PS C:\Workspace\Lambda.Handlers\Lambda.Handlers.Test> sam build
Building codeuri: C:\Workspace\Lambda.Handlers\Lambda.Handlers.Test runtime: None metadata: {'DockerTag': 'local-dev', 'DockerContext': 'C:\\Workspace\\Lambda.Handlers\\Lambda.Handlers.Test', 'Dockerfile': './Dockerfile'} functions: ['TestFunction1']
Building image for TestFunction1 function
Setting DockerBuildArgs: {} for TestFunction1 function
Step 1/3 : FROM public.ecr.aws/lambda/dotnet:5.0
---> 41fed005ba97
Step 2/3 : COPY ./* /var/task/
---> 8269c82eb6dd
Step 3/3 : ENTRYPOINT ["/lambda-entrypoint.sh"]
---> Running in c86af33a921f
---> 3658459b3122
Successfully built 3658459b3122
Successfully tagged testfunction1:local-dev
Building codeuri:
runtime: None metadata: {'DockerTag': 'test-tag', 'DockerContext': 'C:\\Workspace\\Lambda.Handlers\\Lambda.Handlers.Test', 'Dockerfile': './Dockerfile'} functions: ['TestFunction2']
Building image for TestFunction2 function
Setting DockerBuildArgs: {} for TestFunction2 function
Step 1/3 : FROM public.ecr.aws/lambda/dotnet:5.0
---> 41fed005ba97
Step 2/3 : COPY ./* /var/task/
---> Using cache
---> 8269c82eb6dd
Step 3/3 : ENTRYPOINT ["/lambda-entrypoint.sh"]
---> Using cache
---> 3658459b3122
Successfully built 3658459b3122
Successfully tagged testfunction2:test-tag
Build Succeeded
Built Artifacts : .aws-sam\build
Built Template : .aws-sam\build\template.yaml
PS C:\Workspace\Lambda.Handlers\Lambda.Handlers.Test> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
testfunction1 local-dev 3658459b3122 9 minutes ago 403MB
testfunction2 test-tag 3658459b3122 9 minutes ago 403MB
So I'm not really sure if this is sam build working as intended or.... ? I'm not suite sure why its driving that logic based on the DockerTag ? Is it by design ?
@mr-davidc Its working by design. The uniqueness of the build is determined by the all the docker related Metadata, since the Dockerfile contents determine the image, However it does still mean that there could be an UX improvement in this space.
Hmm ok then @sriram-mv . I agree that its not exactly intuitive that the ImageUri properties are essentially ignored for the sam build so perhaps there is some UX or documentation improvements that could be made somewhere. At least now I know that its using the first Logical Resource ID value from the template for the image name and then because all the other Metadata fields match, it will only build a single image, which does seem counterintuitive since I have specified an explicit ImageUri property.
Thanks for the help!
It would be nice to be able to specify the name of the image, instead of it having to be the lowercase version of the function resource name. For example, if my resource name is "MyFunction", it would result in an image name of "myfunction" -- I'd prefer if it were "my-function".
@mr-davidc Hey there I assume some UX improvement on this page https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-build.html is what you expected right? @solarmosaic-kflorence Thanks for bringing this up I will mention your idea to see if we can put this out as a feature!
@qingchm Yes, I think if that page is at least updated to mention all the discovery we did above for this issue ☝️ ,that would be a good start. As @solarmosaic-kflorence said, I think long term, a nice feature would be having the option to specify the image name somehow.
@mr-davidc I see! Thanks for your input!
I would like a way to override the image name as well. It could be as simple as allowing another Metadata field. DockerImageName E.g.
Metadata:
DockerTag: !Ref Tag
DockerContext: .
Dockerfile: Dockerfile
DockerImageName: "my-docker-image-name"
Then in the app_builder.py file via the_build_lambda_image function a simple check to use it, or follow the current convention of the function_name field which is actually the resource name (which is weird)
dockerfile = cast(str, metadata.get("Dockerfile"))
docker_context = cast(str, metadata.get("DockerContext"))
# get the docker image name we want to use
docker_image_name = metadata.get("DockerImageName", None)
# Have a default tag if not present.
tag = metadata.get("DockerTag", "latest")
# FIX: use the custom name if available
if docker_image_name is not None:
docker_tag = f"{cast(str, docker_image_name)}:{tag}"
else:
docker_tag = f"{function_name.lower()}:{tag}"
docker_build_target = metadata.get("DockerBuildTarget", None)
docker_build_args = metadata.get("DockerBuildArgs", {})
Hi @awood45 @sriram-mv @mr-davidc
Does anyone know, why is it generating my images with Name - <none> and Tag - <none>.
I noticed this while investigating the error - Lambda functions containers initialization failed because of Resource ID was not provided while running sam local start-api.
I have below template file:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
Sample SAM Template for HelloWorld
Globals:
Function:
Timeout: 50
MemorySize: 128
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
ImageUri: helloworldfunction
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
Metadata:
DockerTag: !Ref DockerTag
DockerContext: ./src/HelloWorld
Dockerfile: Dockerfile
DockerBuildArgs:
SAM_BUILD_MODE: run
Thanks