skip to Main Content

I have a local folder called images that contains bunch of folders and files within. When I run the container I get the following error:
the command I execute: docker run -t -i file-uploader -token=abcdefgh

panic: failed Walk: Failed to walk directory: *fs.PathError lstat ./images/: no such file or directory

goroutine 1 [running]:
main.main()
        /src/main.go:57 +0x357

Here is the Dockerfile I created:

FROM golang:1.16
WORKDIR /src
COPY go.sum go.mod ./
RUN go mod download
COPY ./images/ images/
COPY . .
RUN CGO_ENABLED=0 go build -o /bin/app .
ENTRYPOINT ["/bin/app"]

FROM scratch
COPY --from=0 /bin/app /bin/app
ENTRYPOINT ["/bin/app"]

And, here is the code in the program:

var (
    token = flag.String("token", "", "user's token for application")
    rootpath = flag.String("rootpath", "./images/","folder path to be uploaded")
)

func main() {
    flag.Parse()

    if *token == "" {
        log.Fatal(Red + "please provide a client token => -token={$token}")
    }

    tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: *token})
    oauthClient := oauth2.NewClient(context.TODO(), tokenSource)
    client := putio.NewClient(oauthClient)

    paths := make(chan string)
    var wg = new(sync.WaitGroup)
    for i := 0; i < 20; i++ {
        wg.Add(1)
        go worker(paths, wg, client)
    }
    if err := filepath.Walk(*rootpath, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return fmt.Errorf("Failed to walk directory: %T %w", err, err)
        }
        if !info.IsDir() {
            paths <- path
        }
        return nil
    }); err != nil {
        panic(fmt.Errorf("failed Walk: %w", err))
    }
    close(paths)
    wg.Wait()
}
}

If flag is not provided, its default value is the folder itself which is ./images/. When I run this normally like: go run main.go -token="abcde", it works properly. I did some changes on Dockerfile. Some of the changes I made and tried again and again.:

  • replacing COPY . . with COPY ./images/ /images. It should automatically creates a folder inside /src like /src/images and get the local folder from host and put into it. It didn’t work.
  • I also did try COPY . ., believing that it will copy everything from host into docker container. It didn’t work either.
  • I did put 2 COPY command together. Didn’t work.
  • COPY . ./ didn’t work either.

My structure of the project is as follows:

/file-uploader-cli
     /images
     Dockerfile
     file-uploader-cli (binary)
     go.mod with go.sum
     main.go

How can I put /images folder into container and run it properly?
Extra question: /images folder is approx. 500 MB or something. Is it a good practice to put that folder into a container?

I guess it is possible to copy a folder like docker cp {$folder_name} ${container_id}:/{$path}, but it must be a running container or something? I did try this using image_id replacing the container_id but I got an error like No such container:path: 12312312312:/.

EDIT:
the problem is the scratch image which was done in order to reduce the size. However, when I delete the scratch thing, the size of the image became 1.1 GB. Any easier or convenient way to utilize the images folder without having too much size?

2

Answers


  1. You don’t need images when building your app, you need it when executing your app. Instead of adding images to the first image, add it to the final image:

    FROM golang:1.16 AS builder
    WORKDIR /src
    COPY go.sum go.mod ./
    RUN go mod download
    COPY . .
    RUN CGO_ENABLED=0 go build -o /bin/app .
    
    FROM scratch
    WORKDIR /
    copy images .
    COPY --from=builder /bin/app /bin/app
    ENTRYPOINT ["/bin/app"]
    

    Or, if you want to provide images dynamically, you could mount it at runtime.

    I also recommend removing docker containers automatically unless you actually want them sticking around. Otherwise you end up with lots and lots of Exited containers.

    docker run --rm -it -v /my/path/to/images:/images:ro file-uploader -token=abcdefgh
    

    I would also recommend you put your token in an environment variable so its not saved in bash history and docker runtime information.

    So, you’re saying don’t dockerize the app?

    I don’t usually containerize Go programs unless containers are a good fit for deployment and operations (eg kubernetes). Most languages like C/C++, Java, Erlang, Python, Javascript, all require significant runtime components provided by the filesystem – compiled ones are usually dynamically linked with shared libraries from the operating system, VM based languages like Java or Erlang require the VM to be installed and configured (and it will likely also have runtime dependencies), and interpreted languages like Python, Ruby, or Javascript require the entire interpreter runtime, as well as any shared libraries the language’s libraries are linked to.

    Go is an exception to this though. Ignoring CGo (which I recommend avoiding whenever possible), Go binaries are statically linked and have minimal userspace runtime requirements. This is why a Go binary is one of the few things that can acutally work in a container built FROM scratch. The one exception to this, is that the Go program will need CA Certificates from the operating system to validate the certificates on HTTPS servers and other protocols secured with TLS.

    I recommend that you don’t dockerize the app unless docker is helping you distribute or operate it.

    Login or Signup to reply.
  2. The problem is that the second build stage does not include the images directory. I don’t think you can use the scratch base for this purpose, so we will have to change that. If you are concerned about image size, you should look into into alpine linux. I have converted your Dockerfile to use alpine.

    FROM golang:1.16-alpine
    WORKDIR /src
    COPY go.sum go.mod ./
    RUN go mod download
    COPY . .
    RUN CGO_ENABLED=0 go build -o /bin/app .
    ENTRYPOINT ["/bin/app"]
    
    FROM alpine:3.15.0
    WORKDIR /opt/app
    COPY --from=0 /bin/app app
    COPY images .
    ENTRYPOINT ["/opt/app/app"]
    

    Note that I changed the app path in the second build stage to /opt/app. I did this because it would be odd to have an image folder under /bin. And /opt is a common place to store user applications.

    As for whether or not you should containerize your code, that is up to you. One of Go’s advantages is static compilation and easy cross-compiling. So you could distribute your binary (and images) as is.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search