0

I have written an Beego HTTP server, that when a user hits an endpoint:

  • the server requests an image from another server (for instance imgur)
  • it reads the bytes of the image and passes them to gographics/imagick
  • this (should) resize the image and return the byte array of the result

what actually happens is my HTTP server completely hangs, I don't even get to the error handling, and I get a 502 bad gateway on all endpoints of the server.

My code looks like this:

func processContactImage(idx int, image []byte) ([]byte, error) {
    imagick.Initialize()
    defer imagick.Terminate()
    log.Println("idx: ", idx)
    mw := imagick.NewMagickWand()

    log.Println("reading image blob: ", image)
    err := mw.ReadImageBlob(image)
    if err != nil {
        log.Println("reading blob failed: ", err)
        return []byte{}, err
    }
//...
}

I can see in the terminal the log message "reading image blob: [bytes, bytes bytes]" and I have copied the bytes printed to another small program to test the bytes do indeed hold an image, they do. It completely hangs on err := mw.ReadImageBlob(image) and I don't think it even gets into the if err != nil as I never see that log message.

Tips on how I should debug this would be welcome. I have written a small program to test the image magick funtions work on the byte array in a standalone enviroment, and it all works fine.

My thoughts:

  • I don't completely understand how Go handles stack/heap, I thought it was able to move things to the heap if necessary and I didn't need to manage this. However I am storing an image in memory, I thought perhaps seg fault but Im not sure why it doesn't crash but hangs...
  • ReadImageBlob is expecting a type of image data, and its not getting it, although I would have then thought it got to the error

EDIT:

OK thanks for comments, after more research, it seems like it is related to the fact I am running this in Docker, it hadn't occured that this could be an issue however:

  1. I have moved the initialization of imagemagick to main and the error still occurs
  2. When I run the application without docker, and I pass a byte array to the handler, imagemagick code runs fine.
  3. When I attach to the docker container, add a small test program that adds a circle to an image using imagemagick (however not a web service, just a binary) it works, albeit very slowly

My dockerfile looks like this:

FROM golang:1.7-alpine

RUN apk update && apk add git && apk add g++ && apk add bzr && \
    rm -rf /var/cache/apk/*

# ENV GOPATH /go

# Install beego & bee
RUN go get github.com/astaxie/beego
RUN go get github.com/beego/bee
RUN go get github.com/tools/godep

RUN apk add --update alpine-sdk
RUN apk add imagemagick-dev
RUN go get gopkg.in/gographics/imagick.v2/imagick

I wonder if I am missing a library or something and its hanging inside the C api and Go is waiting for a response. Is there any way I can debug this?

OK... As it turns out the issue is something else... perhaps multiple requests at once or something... I'm not sure, but I have created this gist demonstrating using imagemagick, in Go, in a handler, and it works locally no problem inside my docker container. The mystery continues....

amlwwalker
  • 3,161
  • 4
  • 26
  • 47
  • Get a stack trace to show exactly where everything is blocked. You can also make a small standalone program that calls `ReadImageBlob(image)` on the exact same input. – JimB Sep 22 '16 at 16:29
  • Any tips on how to force a stacktrace? – amlwwalker Sep 22 '16 at 21:56
  • Send the process a `SIGQUIT`. – JimB Sep 22 '16 at 22:09
  • Go does automatically promote objects to the heap if it detects an escape. `ReadImageBlob` expects a normal `[]byte`. – jdi Sep 22 '16 at 23:23
  • Have you tried the setup in an alternate base docker image, like a full Ubuntu or something? Curious if it is specific to the minimal alpine image. – jdi Sep 23 '16 at 11:48

2 Answers2

2

Don't do this in your handler:

imagick.Initialize()
defer imagick.Terminate()

That is only supposed to be done once, in your main()

Most likely you are conflicting with different requests, by tearing down the entire ImageMagick each time a request finishes.

jdi
  • 90,542
  • 19
  • 167
  • 203
  • Ok I moved this, and the error continued, so I ran more tests and I currently believe the error is docker related, I have updated original question – amlwwalker Sep 23 '16 at 10:31
  • I see. Well I can't say I have any experience running imagick within a docker setup. Although I am puzzled as to why it makes a difference, unless its related to some kind of limitation in the environment. I've run imagick in a web based service that processed images per request and served tons of requests without issue. Albeit not in a dockerized situation. – jdi Sep 23 '16 at 11:45
0

Nice one @jdi it was something to do with Alpine...

Below are my two docker files, the first, with Alpine @612MB doesn't work, it hangs when imagemagick tries to read the blob. The second with golang:latest and 931MB works. I'm not sure why, but it does solve my problem.

Thanks I hope this helps someone else!

FROM golang:1.7-alpine

RUN apk update && apk add git && apk add g++ && apk add bzr && \
    rm -rf /var/cache/apk/*

RUN go get github.com/astaxie/beego
RUN go get github.com/beego/bee
RUN go get github.com/tools/godep

RUN apk add --update alpine-sdk
RUN apk add imagemagick-dev
RUN go get gopkg.in/gographics/imagick.v2/imagick

and the second:

FROM golang:latest

RUN apt-get update && \
    apt-get install -qy \
        pkg-config \
        libmagickcore-dev \
        libmagickwand-dev \
        imagemagick \
        git \
        g++ \
        bzr \

    #clear up
    && apt-get autoremove && \
    apt-get autoclean && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

RUN go get gopkg.in/gographics/imagick.v2/imagick
RUN go get github.com/astaxie/beego
RUN go get github.com/beego/bee
RUN go get github.com/tools/godep 
amlwwalker
  • 3,161
  • 4
  • 26
  • 47
  • Sweet. Well alpine obviously either has some kind of conflicting default configuration or missing something needed by the fact that imagick is a cgo binding and causes your app to become dynamically linked – jdi Sep 23 '16 at 22:14