I have a Go Lambda function. I want to host that function in a Docker image/container so that I can test it locally. In this effort, I have followed the instructions provided here. From those instructions, I have the following files:
.
Dockerfile
go.mod
go.sum
main.go
Those files contain the following:
Dockerfile (a copy of the Dockerfile in this section)
FROM alpine as build
# install build tools
RUN apk add go git
RUN go env -w GOPROXY=direct
# cache dependencies
ADD go.mod go.sum ./
RUN go mod download
# build
ADD . .
RUN go build -o /main
# copy artifacts to a clean image
FROM alpine
COPY --from=build /main /main
ENTRYPOINT [ "/main" ]
go.mod (an updated version of this go.mod)
module main
go 1.18
require (
github.com/aws/aws-lambda-go v1.32.1
github.com/aws/aws-sdk-go v1.44.60
)
require github.com/jmespath/go-jmespath v0.4.0 // indirect
go.sum (a modified version of this go.sum)
github.com/aws/aws-lambda-go v1.32.1 h1:ls0FU8Mt7ayJszb945zFkUfzxhkQTli8mpJstVcDtCY=
github.com/aws/aws-lambda-go v1.32.1/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM=
github.com/aws/aws-sdk-go v1.44.60 h1:KTTogelVR+4dWiIPl7eyxoxaJkziChON6/Y/hVfTipk=
github.com/aws/aws-sdk-go v1.44.60/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
main.go (a copy of this main.go)
package main
import (
"context"
"encoding/json"
"log"
"os"
"github.com/aws/aws-lambda-go/events"
runtime "github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-lambda-go/lambdacontext"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/lambda"
)
var client = lambda.New(session.New())
func callLambda() (string, error) {
input := &lambda.GetAccountSettingsInput{}
req, resp := client.GetAccountSettingsRequest(input)
err := req.Send()
output, _ := json.Marshal(resp.AccountUsage)
return string(output), err
}
func handleRequest(ctx context.Context, event events.SQSEvent) (string, error) {
// event
eventJson, _ := json.MarshalIndent(event, "", " ")
log.Printf("EVENT: %s", eventJson)
// environment variables
log.Printf("REGION: %s", os.Getenv("AWS_REGION"))
log.Println("ALL ENV VARS:")
for _, element := range os.Environ() {
log.Println(element)
}
// request context
lc, _ := lambdacontext.FromContext(ctx)
log.Printf("REQUEST ID: %s", lc.AwsRequestID)
// global variable
log.Printf("FUNCTION NAME: %s", lambdacontext.FunctionName)
// context method
deadline, _ := ctx.Deadline()
log.Printf("DEADLINE: %s", deadline)
// AWS SDK call
usage, err := callLambda()
if err != nil {
return "ERROR", err
}
return usage, nil
}
func main() {
runtime.Start(handleRequest)
}
I can successfully run:
go mod tidy
go build
I can also successfully build and run my Docker image using:
docker build -t lambda-fn .
docker run -d -v ~/.aws-lambda-rie:/aws-lambda --entrypoint /aws-lambda/aws-lambda-rie -p 9000:8080 lambda-fn:latest /main
I can see a container based on the lambda-fn image listed, with a status of Running, in Docker desktop. However, when I send the following cURL request, nothing happens:
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
I was expecting some logs to be written based on the contents of the handleRequest function. What am I doing wrong?
If you are running in demon mode (-d) you cannot see the logs.
Remove -d and rerun the command
This is due the empty handler registration. You can set the handler name by passing the extra argument
docker run -d -v ~/.aws-lambda-rie:/aws-lambda --entrypoint /aws-lambda/aws-lambda-rie -p 9000:8080 lambda-fn:latest /main handleRequest
Related
I am planning to automate spinning up container and run some commands on it.
But I get the below error
docker run -it alpine sh ls
Error I get is
docker error : the input device is not a TTY.
So I removed interactive part and ran
docker run -t alpine sh ls
I don't get the shell but docker is spinning
I run above docker commands in golangs os.exec package.
package main
import (
"fmt"
"os"
"os/exec"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
cmd := exec.Command("docker", "run","-it","alpine","sh","ls")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
// log.Println(cmd.Run())
}()
}()
wg.Wait()
}
My intention is to run multiple shell scripts after spinning up the docker.
Any help would be appreciated. Thanks
One avenue to explore is the official packages moby/moby integration-cli and moby/moby integration-cli/cli with cli.go.
The func Docker(cmd icmd.Cmd, cmdOperators ...CmdOperator) *icmd is made to run a docker command, with parameters.
Example, for docker run:
cli.Docker(cli.Args("run", "--name", name, "--rm", "busybox", "sh", "-c", "sleep 30; echo hi"))
I have a very simple Go app that I'm trying to Dockerize.
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", date)
http.HandleFunc("/health", healthCheck)
http.ListenAndServe(":8080", nil)
}
func healthCheck(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w, "I'm alive!")
}
func date(w http.ResponseWriter, r *http.Request) {
now := time.Now()
fmt.Fprintf(w, now.Format("2006-01-02 15:04:05"))
}
My Dockerfile:
FROM golang:1.14.6
RUN mkdir /app
COPY date.go /app
WORKDIR /app
RUN go build date.go
RUN groupadd -g 999 appuser && \
useradd -r -u 999 -g appuser appuser
USER appuser
EXPOSE 8080
CMD ['/app/date']
However, when I use docker run to run the app, I get the following error:
/bin/sh: 1: [/app/date]: not found
I've commented out the CMD ['/app/date'] line and rebuilt the image, and then was able to exec into it by running
docker run -dit goapp
docker exec -ti [containerid] /bin/bash
This takes me into the /app folder where I do see the date file. And I am able to run /app/date without any issues. I'm not sure what I'm doing wrong.
The CMD is interpreted as JSON, so you need to change the single quotes to double quotes.
CMD ["/app/date"]
This is specified in the Dockerfile documentation:
The exec form is parsed as a JSON array, which means that you must use double-quotes (") around words not single-quotes (').
I am following Go's Docker instructions. Their example works for me. I am now trying to modify it to run a tcp server:
go get github.com/Kelindar/tcp
Add a Dockerfile:
FROM golang
ADD . /go/src/github.com/Kelindar/tcp
RUN go install github.com/Kelindar/tcp
ENTRYPOINT /go/bin/tcp
EXPOSE 8080
I then build it:
docker build --no-cache -t tcp .
build output:
Sending build context to Docker daemon 322kB
Step 1/5 : FROM golang
---> 9fe4cdc1f173
Step 2/5 : ADD . /go/src/github.com/Kelindar/tcp
---> 10abce658324
Step 3/5 : RUN go install github.com/Kelindar/tcp
---> Running in 59dc47b30474
Removing intermediate container 59dc47b30474
---> 8fab53d2882c
Step 4/5 : ENTRYPOINT /go/bin/tcp
---> Running in 18d4b5befccb
Removing intermediate container 18d4b5befccb
---> 073fdb78a481
Step 5/5 : EXPOSE 8080
---> Running in 8ee2b7bc0cba
Removing intermediate container 8ee2b7bc0cba
---> 8bf9f82d4fef
Successfully built 8bf9f82d4fef
Successfully tagged tcp:latest
Now, I run it:
docker run --name test --rm tcp
I get an error:
/bin/sh: 1: /go/bin/tcp: not found
Not really sure where to go on this.
There are two problems here:
The package github.com/Kelindar/tcp does not contain a main function, thus it can't produce a binary to be executed.
There is no main package, and every go program needs a main package with a main function
Whether you run go build or go install in this repository, you will see that nothing will happen, since your package is actually a library.
Now if you add a cmd/tcp_server folder with a tcp_server.go file like this:
package main
import (
"fmt"
"log"
"net"
"github.com/kelindar/tcp"
)
func main() {
closingChan := make(chan bool)
onAccept := func(c net.Conn) {
// Do something here.
}
l, err := net.Listen("tcp", fmt.Sprintf(":%d", 4242))
if err != nil {
log.Fatalf("Unable to net.Listen: %v", err)
}
server := &tcp.Server{
Closing: closingChan,
OnAccept: onAccept,
}
log.Println("Server ready...")
if err := server.Serve(l); err != nil {
log.Fatalf("Server crashed: %v", err)
}
log.Println("Server stopped")
}
And that you update your Dockerfile to use the command that uses your tcp package:
FROM golang
RUN go get github.com/Kelindar/tcp
RUN go build -o $GOPATH/bin/tcp_server $GOPATH/src/github.com/Kelindar/tcp/cmd/tcp_server/tcp_server.go
ENTRYPOINT tcp_server
EXPOSE 8080
Your server will be working properly within docker:
2019/06/16 05:23:29 Server ready...
The error I'm getting for my Go Module project
/bin/sh: microservice: not found
Dockerfile
FROM golang:1.7.4-alpine
MAINTAINER John Doe
ENV SOURCES /go/src/github.com/john/app/
COPY . ${SOURCES}
RUN cd ${SOURCES} && cgo_enabled=0 go install
ENV PORT 8080
EXPOSE 8080
ENTRYPOINT microservice
microservice.go
package main
import (
"fmt"
"net/http"
"os"
)
func main() {
http.HandleFunc("/", index)
http.ListenAndServe(port(), nil)
}
func port() string {
port := os.Getenv("PORT")
fmt.Println(port)
if len(port) == 0 {
port = "8080"
}
return ":" + port
}
func index(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Hello World.")
}
It's a Go module project. I've created an image using following command.
docker build -t app:1.0.3 .
and running it via
docker run -it -p 8080:8080 app:1.0.3
Created executable file is at /go/bin/app
and current working directory is /go.
So, change the last line of your Dockerfile to this
ENTRYPOINT ./bin/app
I've got an akka-http application that works fine locally, I'm having some problems "dockerizing" the application. I build the docker image through the Dockerfile and using a docker-entrypoint to execute the java -jar command. When I firstly access the running docker container the app is not running although If I access the container and manually execute the java -jar command the app starts fine. If I execute the following command (inside the container) the application starts fine as well:
bash -xe docker-entrypoint.sh
See below my Dockerfile
FROM qa.stratio.com/stratio/ubuntu-base:16.04
MAINTAINER stratio
ARG VERSION
RUN apt-get update && apt-get install -y screen
COPY target/khermes-${VERSION}-allinone.jar /khermes.jar
COPY docker/docker-entrypoint.sh /
COPY src/main/resources/application.conf /
EXPOSE 8080
ENTRYPOINT ["/docker-entrypoint.sh"]
And see also below my docker-entrypoint.sh:
#!/bin/bash -xe
java -jar -Dkhermes.client=false -Dakka.remote.hostname=localhost -
Dakka.remote.netty.tcp.port=2553 -Dakka.cluster.seed-
nodes.0=akka.tcp://khermes#localhost:2552 /khermes.jar
tail -f /dev/null
Does anyone have an idea on why my application is getting killed when I run the container?
I recommend using SBT Native Packager instead. My build.scala looks like this
import com.typesafe.sbt.packager.archetypes.JavaAppPackaging
import com.typesafe.sbt.packager.docker.{Cmd, DockerPlugin}
import com.typesafe.sbt.packager.docker.DockerPlugin.autoImport._
import sbt._
import Keys._
object build extends Build {
lazy val root: Project = Project(
id = "my-awesome-server",
base = file("."),
settings = Defaults.coreDefaultSettings ++ Seq(
resolvers ++= {
Seq(
"sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
},
organization := "co.awesome-startup",
version := "2.69",
scalaVersion := "2.11.8",
libraryDependencies ++= {
Seq(
"org.tpolecat" %% "atto-core" % "0.4.2" withSources()
)
},
dockerBaseImage := "expert/docker-java-minimal:server-jre",
dockerCommands := dockerCommands.value.flatMap {
case cmd#Cmd("FROM", _) => List(cmd, Cmd("RUN", "apk update && apk add bash"))
case other => List(other)
},
dockerRepository := Some("awesome-startup"),
version in Docker := version.value,
scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8", "-language:implicitConversions", "-language:postfixOps", "-language:higherKinds", "-Xcheckinit"), //, "-Xlog-implicits"),
javaOptions in compile += "-g:source,lines,vars",
crossPaths := false,
mainClass in Compile := Some("co.awesome-startup.server.EntryPoint")
)
).enablePlugins(JavaAppPackaging).
enablePlugins(DockerPlugin)
}
You'll need to change name of project and dockerRepository to match your repo in Docker Hub and then you can build and deploy it by calling sbt docker:publish. If you want to publish Docker image locally call sbt docker:publishLocal