除非从容器手动启动应用程序,否则Go无法从容器中写入文件。

huangapple go评论90阅读模式
英文:

go does not write file from container unless application is manually started from container

问题

我写了一个小的Go应用程序,它有两个功能:

  • 处理 http://localhost:5000
  • 每秒写入一条日志

这个应用程序在一个Docker容器中。你可以看到的日志记录器是我的,只是一个练习。应用程序已经启动。如果我使用curl命令访问 http://localhost:5000,我可以看到"Ciao, mondo!"。但是日志记录器没有启动。奇怪的是,如果我进入容器,并手动运行应用程序,日志记录器就开始记录了。我认为这可能是权限的问题。

以下是docker-compose文件:

version: "3.9"
services:
    app:
        container_name: 'go_docker_app'
        build:
            dockerfile: Dockerfile
            context: .
        volumes:
            - ./logs:/app/logs
            - .:/opt/app/api
        ports:
            - "5000:5000"

以下是main.go文件的内容:

package main

import (
        "fmt"
        "net/http"
        "time"
        "github.com/sensorario/gol"
)

func main() {
    http.HandleFunc("/", HelloServer)
    http.ListenAndServe(":5000", nil)

    go forever()
    select{}
}

func HelloServer(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Ciao, mondo!")
}

func forever() {
    l := gol.NewCustomLogger("/app")
    for {
        l.Info(fmt.Sprintf("%v+\n", time.Now()))
        time.Sleep(time.Second)
    }
}

以下是Dockerfile的内容:

我尝试了多种解决方案。例如,从Dockerfile中创建logger.log文件。我不认为这是权限的问题,因为我没有收到任何错误信息。

FROM golang:1.17-alpine
RUN mkdir -p /app/logs
ADD . /app
WORKDIR /app
RUN go get -d -v ./...
RUN go install -v ./...
RUN go build -o /application
EXPOSE 5000
RUN touch /app/logs/logger.log
CMD ["/application"]
英文:

I've written a little golang application. It do two things:

  • handle http://localhost:5000
  • write a log each seconds

This application is inside a docker container. The logger you can see is mine, is just an exercise. Application start. If I curl http://localhost:5000 I can see "Ciao, mondo!". But the logger does not starts. The strange thing is that if I go into the container, and I run the application by hand. The logger start to log. I thought this can be a problem of permissions.

Here the docker-compose.

version: "3.9"
services:
    app:
        container_name: 'go_docker_app'
        build:
            dockerfile: Dockerfile
            context: .
        volumes:
            - ./logs:/app/logs
            - .:/opt/app/api
        ports:
            - "5000:5000"

Here the main.go file

package main

import (
        "fmt"
        "net/http"
        "time"
        "github.com/sensorario/gol"
)

func main() {
    http.HandleFunc("/", HelloServer);
    http.ListenAndServe(":5000", nil);

    go forever()
    select{}
}

func HelloServer(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Ciao, mondo!");
}

func forever() {
    l := gol.NewCustomLogger("/app");
    for {
        l.Info(fmt.Sprintf("%v+\n", time.Now()));
        time.Sleep(time.Second)
    }
}

Here the Dockerfile

I've tried more solutions. For example creating logger.log from Dockerfile. I dont think is a problem of permissions because I have no errors.

FROM golang:1.17-alpine
RUN mkdir -p /app/logs
ADD . /app
WORKDIR /app
RUN go get -d -v ./...
RUN go install -v ./...
RUN go build -o /application
EXPOSE 5000
RUN touch /app/logs/logger.log
CMD ["/application"]

答案1

得分: 3

这是由于在你的goroutine之前运行了ListenAndServe导致的。go forever()从未执行,因此不会输出任何日志文件。

奇怪的是,如果我进入容器,手动运行应用程序,记录器就开始记录了。

我猜你在运行的容器上执行了命令,其中一个应用程序实例已经在运行,所以端口5000已经被占用。你没有进行任何错误处理,所以它跳过了错误并进入了forever()函数。空的select{}是不需要的,因为ListenAndServe会“阻塞”程序:一个很好的解释可以在这里找到https://stackoverflow.com/a/44598343/14484111

func main() {

    go forever()
    http.HandleFunc("/", HelloServer);

    // 添加错误验证
    err := http.ListenAndServe(":5000", nil);
    log.Fatal(err)

    // 或者更简洁的写法
    // log.Fatal(http.ListenAndServe(":5000", nil))

}
英文:

It's caused by running ListenAndServe before your goroutine. go forever() never execute, so it will not output any log file.

> The strange thing is that if I go into the container, and I run the application by hand. The logger start to log.

I assume that You exec on running container, where one instance of your app is alredy running so the Port 5000 is alredy taken. You don't have any error handling, so it skips it and go to the forever() function. Empty select{} won't be needed, coz ListenAndServe will "block" the program: a nice explenation https://stackoverflow.com/a/44598343/14484111

func main() {

    go forever()
    http.HandleFunc("/", HelloServer);

    // added error validating
    err := http.ListenAndServe(":5000", nil);
    log.Fatal(err)

    // or shorter version
    // log.Fatal(http.ListenAndServe(":5000", nil))

}

huangapple
  • 本文由 发表于 2021年10月27日 05:12:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/69730019.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定