aws-sam-cli icon indicating copy to clipboard operation
aws-sam-cli copied to clipboard

Lambda Container Image support in SAM CLI invoke local

Open okonon opened this issue 3 years ago • 19 comments

Description:

Seems like sam local invoke -t .cfr-template.yaml --no-event myfunc does not work.

Steps to reproduce:

create lambda with from local dockerfile and try to run sam local invoke

Observed result:

2020-12-07 14:29:10,526 | Failed to download image with name 123456789012.dkr.ecr.us-west-2.localhost/aws-cdk/assets:rapid-1.13.1
2020-12-07 14:29:10,526 | Container was not created. Skipping deletion
2020-12-07 14:29:10,527 | Sending Telemetry: {'metrics': [{'commandRun': {'awsProfileProvided': True, 'debugFlagProvided': True, 'region': 'us-west-2', 'commandName': 'sam local invoke', 'duration': 901, 'exitReason': 'DockerImagePullFailedException', 'exitCode': 1, 'requestId': '0b2dd4d8-261f-475d-acc0-811c986e4e8e', 'installationId': 'e2800af1-28a2-4757-8c89-097af3d3460d', 'sessionId': 'fd6eaa05-8e6c-4d9f-af64-983f77b1c7b3', 'executionEnvironment': 'CLI', 'pyversion': '3.8.6', 'samcliVersion': '1.13.1'}}]}
2020-12-07 14:29:11,016 | HTTPSConnectionPool(host='aws-serverless-tools-telemetry.us-west-2.amazonaws.com', port=443): Read timed out. (read timeout=0.1)
Error: Could not find 123456789012.dkr.ecr.us-west-2.localhost/aws-cdk/assets:rapid-1.13.1 image locally and failed to pull it from docker.

That localhost in ECR path looks weird

Expected result:

Function would execute locally

Additional environment details (Ex: Windows, Mac, Amazon Linux etc)

  1. OS: Mac OS 11.0.1 (20B50)
  2. sam --version: SAM CLI, version 1.13.1

okonon avatar Dec 07 '20 19:12 okonon

@okonon What does your template look like? We get the image to use from the template, which appears to be something like 123456789012.dkr.ecr.us-west-2.localhost/aws-cdk/assets. By the error, it seems like that does not exist at all.

jfuss avatar Dec 09 '20 17:12 jfuss

@jfuss thanks for quick reply. Here is a part of the tampate that contstructs that image uri and it looks like UrlSuffix is resolved as loocalhost:

DEVDevFnTestFE722C10:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ImageUri:
          Fn::Join:
            - ""
            - - Ref: AWS::AccountId
              - .dkr.ecr.
              - Ref: AWS::Region
              - "."
              - Ref: AWS::URLSuffix
              - /aws-cdk/assets:642dc1b6406c0b5059505935a0752fe51a8bbc681df58dfe811c26f3c60f0e14
      Role:
        Fn::GetAtt:
          - DEVDevFnTestServiceRoleF39BC457
          - Arn
      Description: DEVTHETech-Get lambda docker function
      Environment:
        Variables:
          NODE_ENV: DEV
      FunctionName: DEVTHETech-Get
      PackageType: Image
      Timeout: 60
    DependsOn:
      - DEVDevFnTestServiceRoleDefaultPolicy0006F43A
      - DEVDevFnTestServiceRoleF39BC457
    Metadata:
      aws:cdk:path: DEVARTechServices/DEVTHETech-Get/DEVTHETech-Get/Resource
  DEVDevFnTestLogRetention86D05CD3:
    Type: Custom::LogRetention
    Properties:
      ServiceToken:
        Fn::GetAtt:
          - LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A
          - Arn
      LogGroupName:
        Fn::Join:
          - ""
          - - /aws/lambda/
            - Ref: DEVDevFnTestFE722C10
      RetentionInDays: 14
    Metadata:
      aws:cdk:path: DEVTHETechServices/DEVTHETech-Get/DEVTHETech-Get/LogRetention/Resource

okonon avatar Dec 14 '20 14:12 okonon

I have seen this same behavior. the call does not resolve AWS::AccountId. If you pass in the full URL to the container and not use the !Sub it will execute the container. BUT, it doesn't seem to use the ImageConfig settings like

Command: - /lambda-entrypoint.sh - api.get_metadata

agilesrcmh avatar Dec 15 '20 21:12 agilesrcmh

Note for those that need a 'fix' to this. If you lambda is 100% container you can test this way.

  1. download the container from ECR (lets call it mycontainer:mytag)
  2. run docker
docker run -p 9000:8080 --entrypoint "/lambda-entrypoint.sh" \
  --env SOME_ENV=myvalue \
  -v ~/.aws/config:/root/.aws/config \
  myaccount.dkr.ecr.us-east-1.amazonaws.com/mycontainer:mytag api.metadata_changelog
  1. in a different window run curl against the container
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"something": "value"}'

This is based off : https://aws.amazon.com/blogs/aws/new-for-aws-lambda-container-image-support/

agilesrcmh avatar Dec 15 '20 23:12 agilesrcmh

~~@agilesrcmh Thanks for the heads up! I'm working through this, and for the life of me can't figure out what process.env has nothing for a nodejs12.x based Lambda container. @jfuss Does this work for you, do you have any thoughts?~~

~~My Dockerfile for the lambda is boring, it is pointed at JS after having been built using Parcel;~~

Edit: Parcel was building my code and I didn't realize it disallows process.env .. I wanted to avoid Webpack, but I think it's better than Parcel for the needs of exposing process.env to the Lambda.

revgum avatar Dec 20 '20 17:12 revgum

in my case i have not used any bundling tools. Straight JS packaged in a container

okonon avatar Dec 21 '20 19:12 okonon

Oh also in my case i am using AWS CDK to generate CF template

okonon avatar Dec 21 '20 19:12 okonon

@okonon Does CDK actually produce an image locally that would work if this was fully resolved:

Fn::Join:
            - ""
            - - Ref: AWS::AccountId
              - .dkr.ecr.
              - Ref: AWS::Region
              - "."
              - Ref: AWS::URLSuffix
              - /aws-cdk/assets:642dc1b6406c0b5059505935a0752fe51a8bbc681df58dfe811c26f3c60f0e14

SAM CLI doesn't have a way to lookup your account, so it is getting replaced with just a 'default' fake accountId. Even is we did fully resolve this, CDK would have to have produced an image that is local that could be used to run locally. I don't know how CDK implemented this, but my guess is CDK is assuming the build process is something the customer does (I think that is how zips work, or at least did last time I looked at it).

There isn't much SAM CLI can do here, given you are trying to bridge gaps between tools.

jfuss avatar Dec 21 '20 21:12 jfuss

CDK does produce the image locally and pushed it to ECR with asset hash as a tag i can see images there and if i build the image locally it does run fine and i can reach it using their documented endpoint: http://localhost:9000/2015-03-31/functions/function/invocations I do understand that these are different tools but they explicitly state in their documentation that one should use sam local commands to test locally https://docs.aws.amazon.com/cdk/latest/guide/sam.html it is also all over cdk sample projects

okonon avatar Dec 22 '20 01:12 okonon

@jfuss I just ran it again after updating CDK to latest 1.79.0 and still getting same error

> sam local invoke --region us-west-2 -t cdk.out/*.template.json --no-event DEVGetParts

Invoking Container created from <ACCOUNT_ID_IS_HIDDEN_FOR_PRIVACY>.dkr.ecr.us-west-2.localhost/aws-cdk/assets:642dc1b6406c0b5059505935a0752fe51a8bbc681df58dfe811c26f3c60f0e14

I can see that my account ID is resolved correctly and in the log above i removed it manually. I also can see that asset in ECR: image

okonon avatar Dec 22 '20 03:12 okonon

so i think account id is resolved correctly but, the only difference between image ARNs is the host name: sam cli attempts to pull from <ACCOUNT_ID_IS_HIDDEN_FOR_PRIVACY>.dkr.ecr.us-west-2.localhost/aws-cdk/assets:642dc1b6406c0b5059505935a0752fe51a8bbc681df58dfe811c26f3c60f0e14 (note localhost in arn instead of amazonaws.com ) and fails with following message: Error response from daemon: Get https://<ACCOUNT_ID_IS_HIDDEN_FOR_PRIVACY>.dkr.ecr.us-west-2.localhost/v2/: Service Unavailable

Upon further research this it seems that AWS::URLSuffix resolves to localhost instead of amazonaws.com.

However when i query above image:

aws ecr describe-images --repository-name aws-cdk/assets --image-ids imageTag=642dc1b6406c0b5059505935a0752fe51a8bbc681df58dfe811c26f3c60f0e14

I get following response:

{
    "imageDetails": [
        {
            "artifactMediaType": "application/vnd.docker.container.image.v1+json", 
            "imageSizeInBytes": 146746893, 
            "imageDigest": "sha256:9014e9f2bf720d0b5d1f58d4242d38d0c2331af546609ad4be0547a3cec638b2", 
            "imageManifestMediaType": "application/vnd.docker.distribution.manifest.v2+json", 
            "imageTags": [
                "642dc1b6406c0b5059505935a0752fe51a8bbc681df58dfe811c26f3c60f0e14"
            ], 
            "registryId": "<ACCOUNT_ID_IS_HIDDEN_FOR_PRIVACY>", 
            "imageScanStatus": {
                "status": "COMPLETE", 
                "description": "The scan was completed successfully."
            }, 
            "imageScanFindingsSummary": {
                "imageScanCompletedAt": 1607721771.0, 
                "vulnerabilitySourceUpdatedAt": 1604452869.0, 
                "findingSeverityCounts": {}
            }, 
            "repositoryName": "aws-cdk/assets", 
            "imagePushedAt": 1607721765.0
        }
    ]
}

and then i can successfully pull the image:

docker pull <ACCOUNT_ID_IS_HIDDEN_FOR_PRIVACY>.dkr.ecr.us-west-2.amazonaws.com/aws-cdk/assets:642dc1b6406c0b5059505935a0752fe51a8bbc681df58dfe811c26f3c60f0e14

And here is the docker pull output:

642dc1b6406c0b5059505935a0752fe51a8bbc681df58dfe811c26f3c60f0e14: Pulling from aws-cdk/assets
60da425939d5: Already exists 
eb91b8d014d7: Already exists 
b466e1635c1c: Already exists 
03ac043af787: Already exists 
30f88e0e12ab: Already exists 
b6702f7385e4: Already exists 
20aa5987a13e: Pull complete 
f3e8f0d13412: Pull complete 
Digest: sha256:9014e9f2bf720d0b5d1f58d4242d38d0c2331af546609ad4be0547a3cec638b2
Status: Downloaded newer image for <ACCOUNT_ID_IS_HIDDEN_FOR_PRIVACY>.dkr.ecr.us-west-2.amazonaws.com/aws-cdk/assets:642dc1b6406c0b5059505935a0752fe51a8bbc681df58dfe811c26f3c60f0e14
<ACCOUNT_ID_IS_HIDDEN_FOR_PRIVACY>.dkr.ecr.us-west-2.amazonaws.com/aws-cdk/assets:642dc1b6406c0b5059505935a0752fe51a8bbc681df58dfe811c26f3c60f0e14

okonon avatar Dec 22 '20 04:12 okonon

More info. Sorry for a lot of messages. After replacing all AWS::URLSuffix instances in my template with hardcoded amazonaws.com value i am successfully able to run lambda locally by invoke local command or start api command:

sam local start-api -d 5858 -t cdk.out/*.template.json

Mounting None at http://127.0.0.1:3000/ [OPTIONS]
Mounting None at http://127.0.0.1:3000/parts [OPTIONS]
Mounting DEVARTech-GetParts at http://127.0.0.1:3000/parts [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2020-12-21 23:31:12  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
Invoking Container created from <ACCOUNT_ID_IS_HIDDEN_FOR_PRIVACY>.dkr.ecr.us-west-2.amazonaws.com/aws-cdk/assets:642dc1b6406c0b5059505935a0752fe51a8bbc681df58dfe811c26f3c60f0e14
Building image.........
Skip pulling image and use local one: <ACCOUNT_ID_IS_HIDDEN_FOR_PRIVACY>.dkr.ecr.us-west-2.amazonaws.com/aws-cdk/assets:rapid-1.13.1.

START RequestId: 67facec9-0ad7-4118-909c-5eacb9ad1cd1 Version: $LATEST
} version: '1.0': null,ts',11471,31:11 +0000',075e1c',d8dbdc' ],1cd1    INFO    {
END RequestId: 67facec9-0ad7-4118-909c-5eacb9ad1cd1
REPORT RequestId: 67facec9-0ad7-4118-909c-5eacb9ad1cd1  Init Duration: 0.20 ms  Duration: 117.65 ms     Billed Duration: 200 ms Memory Size: 128 MB     Max Memory Used: 128 MB
No Content-Type given. Defaulting to 'application/json'.
2020-12-21 23:31:23 127.0.0.1 - - [21/Dec/2020 23:31:23] "GET /parts HTTP/1.1" 200 -

okonon avatar Dec 22 '20 04:12 okonon

This looks to be related to intrinsics more than anything else, SAM CLI attempts to resolve locally and substitute values and this is why it doesn't work.

sriram-mv avatar Feb 17 '21 23:02 sriram-mv

I'm running into the same issue trying to run

sam local start-api --template-file sam-template.yaml

where my sam-template.yaml is generated by a cdk synth command. This pattern worked fine when I was using non-container backed lambdas. Updating my cdk stack to use container-backed lambdas totally breaks my sam local deployment.

I agree with the diagnosis above that the issue is that the Refs generated by CDK aren't being resolved by lambda. Snippet of my template:

  MyLambda:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ImageUri:
          Fn::Join:
            - ""
            - - Ref: AWS::AccountId
              - .dkr.ecr.
              - Ref: AWS::Region
              - "."
              - Ref: AWS::URLSuffix
              - /my-ecr-repo:my-image-tag
      Role:
        Fn::GetAtt:

I think the crux of the issue is that container-backed lambdas seem to only work with ECR, rather than allowing for purely local image assets. Maybe I am wrong there? My ideal solution would be to be able to generate a template from cdk that doesn't depend on ECR at all so that sam can work with only local assets

alex9311 avatar Jul 15 '21 00:07 alex9311

@alex9311 not sure if you was this document but there is a way to locally build and execute your lambda container based function. I used it as a workaround for this issue. It is raw lambda request/response flow and it will not have any of features that API Gateway provides running locally through SAM.

okonon avatar Jul 15 '21 20:07 okonon

So i tried and revisit this issue on my recent project and this still is not working. Seems that sam cli does not support rest api lambda that are based on docker still. Is there anything else that i can do to help?

okonon avatar Oct 22 '21 02:10 okonon

This is still a problem with SAM CLI, version 1.66.0. I'm also using CDK with a Docker Image-based Lambda (vs. a locally created docker image)

mdnorman avatar Dec 15 '22 04:12 mdnorman

I ran into this today. I'm using SAM CLI, version 1.105.0.

It was believed to be the limitation of intrinsic function and one workaround, as mentioned in a few comments above, was to replace URLSuffix with hardcoded amazonaws.com. However, it does not seem to work anymore.

I believe it's blocked by this: https://github.com/aws/aws-sam-cli/blob/034e027b4010a709d101a035fa41813c63bc790e/samcli/lib/providers/sam_function_provider.py#L222-L233

It looks like if it's SAM template with correct Metadata, it's still possible to move forward. But not for cdk template.

cfchou avatar Jan 01 '24 03:01 cfchou

Got blocked by this today. Its really a shame that CDK and SAM are incompatible with each other for local container testing and there are no workarounds provided in this thread.

deleugpn avatar Aug 22 '24 15:08 deleugpn