lifadev / archive_aws-lambda-go-shim

Author your AWS Lambda functions in Go, effectively.
https://github.com/eawsy/aws-lambda-go-shim
Apache License 2.0
789 stars 66 forks source link

Ensure pkg cache is preserved between builds #39

Closed samcday closed 6 years ago

samcday commented 7 years ago

First up, awesome project. Huge fan! :)

I started using it but quickly noticed that the builds for my Lambda function were getting crazy slow as I imported more packages. I did a bit of digging and it turns out running go build buildmode=plugin doesn't properly cache the pkg/ artifacts unless you tell it to with the -i option. That limitation is kinda hidden away in golang/go#19707 and I didn't find it until the third or fourth frustrated google search ;)

So what this does is adds the -i option to the build command, and ensures we have a data volume attached to /usr/local/go/pkg/linux_amd64_dynlink in the container so that the pkg cache is reused between builds.

Before this change:

$ time make docker
docker run --rm\
  -e HANDLER=handler\
  -e PACKAGE=handler\
  -e GOPATH=/home/sam\
  -e LDFLAGS=''\
  -v /home/sam/src/github.com/samcday/scrobbify/backend:/home/sam/src/github.com/samcday/scrobbify/backend\
  -v /home/sam:/home/sam\
  -w /home/sam/src/github.com/samcday/scrobbify/backend\
  eawsy/aws-lambda-go-shim:latest make -f Makefile all
go build -v -buildmode=plugin -ldflags='-w -s ' -o handler.so
runtime/internal/sys
runtime/internal/atomic
runtime
errors
internal/race
sync/atomic
internal/cpu
unicode/utf8
unicode
encoding
unicode/utf16
math/bits
container/list
sync
crypto/subtle
crypto/internal/cipherhw
math
internal/nettrace
vendor/golang_org/x/crypto/poly1305
vendor/golang_org/x/crypto/curve25519
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/client/metadata
github.com/samcday/scrobbify/vendor/github.com/eawsy/aws-lambda-go-core/service/lambda/runtime
io
syscall
internal/singleflight
hash
crypto/cipher
hash/crc32
crypto/hmac
strings
bytes
bufio
vendor/golang_org/x/text/transform
strconv
path
math/rand
time
internal/syscall/unix
reflect
crypto/rc4
crypto/aes
crypto
encoding/base64
crypto/sha512
crypto/sha1
crypto/md5
crypto/sha256
internal/poll
os
fmt
encoding/binary
sort
path/filepath
regexp/syntax
encoding/pem
crypto/des
vendor/golang_org/x/crypto/chacha20poly1305/internal/chacha20
vendor/golang_org/x/crypto/chacha20poly1305
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/internal/shareddefaults
io/ioutil
context
compress/flate
encoding/json
log
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/awserr
math/big
encoding/hex
vendor/golang_org/x/net/http2/hpack
vendor/golang_org/x/text/unicode/norm
net/url
vendor/golang_org/x/text/unicode/bidi
regexp
mime
mime/quotedprintable
net/http/internal
compress/gzip
runtime/cgo
vendor/golang_org/x/text/secure/bidirule
encoding/xml
github.com/samcday/scrobbify/vendor/github.com/go-ini/ini
github.com/samcday/scrobbify/vendor/golang.org/x/net/context
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/endpoints
github.com/samcday/scrobbify/vendor/github.com/jmespath/go-jmespath
github.com/samcday/scrobbify/vendor/github.com/eawsy/aws-lambda-go-event/service/lambda/runtime/event/apigatewayproxyevt
vendor/golang_org/x/net/idna
crypto/rand
crypto/elliptic
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/credentials
encoding/asn1
crypto/rsa
crypto/dsa
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/awsutil
crypto/ecdsa
crypto/x509/pkix
net
crypto/x509
vendor/golang_org/x/net/lex/httplex
vendor/golang_org/x/net/proxy
github.com/samcday/scrobbify/vendor/github.com/eawsy/aws-lambda-go-net/service/lambda/runtime/net
net/textproto
mime/multipart
crypto/tls
github.com/samcday/scrobbify/vendor/github.com/dgrijalva/jwt-go
net/http/httptrace
net/http
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws
net/http/httputil
github.com/samcday/scrobbify/vendor/github.com/eawsy/aws-lambda-go-net/service/lambda/runtime/net/apigatewayproxy
github.com/samcday/scrobbify/vendor/golang.org/x/net/context/ctxhttp
github.com/samcday/scrobbify/vendor/golang.org/x/oauth2/internal
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/request
github.com/samcday/scrobbify/vendor/golang.org/x/oauth2
github.com/samcday/scrobbify/vendor/github.com/zmb3/spotify
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/client
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/private/protocol
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/private/protocol/rest
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/corehandlers
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/private/protocol/query/queryutil
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/credentials/endpointcreds
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/signer/v4
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/private/protocol/query
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/defaults
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/service/sts
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/service/dynamodb
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/aws/session
github.com/samcday/scrobbify/vendor/github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute
github.com/samcday/scrobbify/backend
pack handler handler.so handler.zip
chown 1000:1000 handler.so handler.zip
make docker 0.01s user 0.03s system 0% cpu 6.252 total

After:

$ time make docker
docker run --rm\
  -e HANDLER=handler\
  -e PACKAGE=handler\
  -e GOPATH=/home/sam\
  -e LDFLAGS=''\
  -v lambdagoshimcache:/usr/local/go/pkg/linux_amd64_dynlink\
  -v /home/sam/src/github.com/samcday/scrobbify/backend:/home/sam/src/github.com/samcday/scrobbify/backend\
  -v /home/sam:/home/sam\
  -w /home/sam/src/github.com/samcday/scrobbify/backend\
  eawsy/aws-lambda-go-shim:latest make -f Makefile all
go build -v -i -buildmode=plugin -ldflags='-w -s ' -o handler.so
github.com/samcday/scrobbify/backend
pack handler handler.so handler.zip
chown 1000:1000 handler.so handler.zip
make docker 0.04s user 0.02s system 2% cpu 1.904 total
fsenart commented 7 years ago

Hi @samcday, Whaoo what a useful PR. I haven't even heard about this option until now. Very thankful for your PR. I will merge it in the next few days, I have to perform some tests, etc. and will come back soon.

samcday commented 7 years ago

@fsenart no probs! Let me know if I can help any further

fsenart commented 6 years ago

Hi @samcday,

Please apologize for my unavailability. I was under a very heavy workload past month.

I reviewed very carefully this optimization and here are my remarks:

It brings us to the following updated Makefile. Please pay attention to the following points:

HANDLER ?= handler
PACKAGE ?= $(HANDLER)

ifeq ($(OS),Windows_NT)
    GOPATH ?= $(USERPROFILE)/go
    GOPATH := /$(subst ;,:/,$(subst \,/,$(subst :,,$(GOPATH))))
    CURDIR := /$(subst :,,$(CURDIR))
    PKGDIR ?= $(CURDIR)/.cache
    RM := del /s /q
else
    GOPATH ?= $(HOME)/go
    PKGDIR ?= $(CURDIR)/.cache
    RM := rm -rf
endif

MAKEFILE = $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))

docker:
    docker run --rm\
        -e HANDLER=$(HANDLER)\
        -e PACKAGE=$(PACKAGE)\
        -e GOPATH=$(GOPATH)\
        -e LDFLAGS='$(LDFLAGS)'\
        -v $(CURDIR):$(CURDIR)\
        -v $(PKGDIR):/cache\
        $(foreach GP,$(subst :, ,$(GOPATH)),-v $(GP):$(GP))\
        -w $(CURDIR)\
        eawsy/aws-lambda-go-shim:latest make -f $(MAKEFILE) all

.PHONY: docker

all: build pack perm

.PHONY: all

build:
    go build -v -buildmode=plugin -ldflags='-w -s $(LDFLAGS)' -pkgdir=/cache -i -o $(HANDLER).so

.PHONY: build

pack:
    pack $(HANDLER) $(HANDLER).so $(PACKAGE).zip

.PHONY: pack

perm:
    chown -R $(shell stat -c '%u:%g' .) $(HANDLER).so $(PACKAGE).zip /cache

.PHONY: perm

clean:
    $(RM) $(HANDLER).so $(PACKAGE).zip $(PKGDIR)

.PHONY: clean

Let me know what do you think about these remarks. And hopefully we will merge this huge optimization very soon. Thank you again for your time and for your contribution.

fsenart commented 6 years ago

See also badass improvements coming with Go 1.10.

samcday commented 6 years ago

Sorry @fsenart - Github doesn't seem to notify me via email anymore.

Your changes LGTM, much more comprehensive approach to the optimization :)

fsenart commented 6 years ago

@samcday can you please update your patch in accordance so that I can merge this long running PR :smile:

PS: In the meantime I will try to find a windows host to be sure that the PKGDIR works as expected.

lion3ls commented 6 years ago

Works on Windows 10 👍.

For windows compatibility some modifications are needed, here is my makefile working on both Windows and Linux:

HANDLER ?= handler
PACKAGE ?= $(HANDLER)
CACHEDIR ?= .cache

ifeq ($(OS),Windows_NT)
    GOPATH ?= $(USERPROFILE)/go
    GOPATH := /$(subst ;,:/,$(subst \,/,$(subst :,,$(GOPATH))))
    CURDIR := /$(subst :,,$(CURDIR))
    PKGDIR ?= $(CURDIR)/$(CACHEDIR)
    RM := del /S /Q
    RMDIR := rd /S /Q
else
    GOPATH ?= $(HOME)/go
    PKGDIR ?= $(CURDIR)/$(CACHEDIR)
    RM := rm -rf
    RMDIR := rm -rf
endif

MAKEFILE = $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))

docker:
    docker run --rm\
        -e HANDLER=$(HANDLER)\
        -e PACKAGE=$(PACKAGE)\
    -e GOPATH=$(GOPATH)\
    -e LDFLAGS='$(LDFLAGS)'\
    -v $(CURDIR):$(CURDIR)\
    -v $(PKGDIR):/cache\
    $(foreach GP,$(subst :, ,$(GOPATH)),-v $(GP):$(GP))\
    -w $(CURDIR)\
    eawsy/aws-lambda-go-shim:latest make -f $(MAKEFILE) all

.PHONY: docker

all: build pack perm

.PHONY: all

build:
    go build -v -buildmode=plugin -ldflags='-w -s $(LDFLAGS)' -pkgdir=/cache -i -o $(HANDLER).so

.PHONY: build

pack:
    pack $(HANDLER) $(HANDLER).so $(PACKAGE).zip

.PHONY: pack

perm:
    chown -R $(shell stat -c '%u:%g' .) $(HANDLER).so $(PACKAGE).zip /cache

.PHONY: perm

clean:
    $(RM) $(HANDLER).so $(PACKAGE).zip
    $(RMDIR) $(CACHEDIR)

.PHONY: clean

Can't wait on your final PR @samcday 😉

fsenart commented 6 years ago

Thank you very much @samcday. Sadly, we haven't had a chance to integrate this awesome contribution as the new official go runtime is out 🎉