使用 Golang + Gin + Docker 时出现“localhost 没有发送任何数据”的错误。

4

我根据一个YouTube教程创建了一个简单的API,在本地正常运行。但是一旦我将应用程序容器化并运行容器,就无法访问http://localhost:8080上的API。我猜想这与我在dockerfile中使用的端口设置有关,但我不确定。

main.go文件:

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
    "errors"
)

type phone struct{
    ID       string `json:"id"`
    Model    string `json:"model"`
    Year     string `json:"year"`
    Quantity int    `json:"quantity"`
}

var phones = []phone{
    {ID: "1", Model: "iPhone 11", Year: "2019", Quantity: 4},
    {ID: "2", Model: "iPhone 6", Year: "2014", Quantity: 9},
    {ID: "3", Model: "iPhone X", Year: "2017", Quantity: 2},
}

func phoneById(c *gin.Context) {
    id := c.Param("id")
    phone, err := getPhoneById(id)

    if err != nil {
        c.IndentedJSON(http.StatusNotFound, gin.H{"message": "Phone not found."})
        return
    }

    c.IndentedJSON(http.StatusOK, phone)
}

func checkoutPhone(c *gin.Context) {
    id, ok := c.GetQuery("id")

    if !ok {
        c.IndentedJSON(http.StatusBadRequest, gin.H{"Message": "Missing id query paramater"})
        return
    }

    phone, err := getPhoneById(id)

    if err != nil {
        c.IndentedJSON(http.StatusBadRequest, gin.H{"Message": "Phone not found"})
        return
    }

    if phone.Quantity <= 0 {
        c.IndentedJSON(http.StatusBadRequest, gin.H{"Message": "Phone not available."})
        return
    }

    phone.Quantity -= 1
    c.IndentedJSON(http.StatusOK, phone)
}

func returnPhone(c *gin.Context) {
    id, ok := c.GetQuery("id")

    if !ok {
        c.IndentedJSON(http.StatusBadRequest, gin.H{"Message": "Missing id query paramater"})
        return
    }

    phone, err := getPhoneById(id)

    if err != nil {
        c.IndentedJSON(http.StatusBadRequest, gin.H{"Message": "Phone not found"})
        return
    }

    if phone.Quantity <= 0 {
        c.IndentedJSON(http.StatusBadRequest, gin.H{"Message": "Phone not available."})
        return
    }

    phone.Quantity += 1
    c.IndentedJSON(http.StatusOK, phone)
}

func getPhoneById(id string) (*phone, error) {
    for i, p := range phones {
        if p.ID == id {
            return &phones[i], nil
        }
    }

    return nil, errors.New("Phone not found.")
}


func getPhones(c *gin.Context) {
    c.IndentedJSON(http.StatusOK, phones)
}

func createPhone(c *gin.Context) {
    var newPhone phone

    if err := c.BindJSON(&newPhone); err != nil {
        return 
    }

    phones = append(phones, newPhone)
    c.IndentedJSON(http.StatusCreated, newPhone)
}

func main(){
    router := gin.Default()
    router.GET("/phones", getPhones)
    router.GET("/phones/:id", phoneById)
    router.POST("/phones", createPhone)
    router.PATCH("/checkout", checkoutPhone)
    router.PATCH("/return", returnPhone)
    router.Run("localhost:8080")
}

我的Dockerfile:

#The standard golang image contains all of the resources to build
#But is very large.  So build on it, then copy the output to the
#final runtime container
FROM golang:latest AS buildContainer
WORKDIR /go/src/app

COPY . .

#flags: -s -w to remove symbol table and debug info
#CGO_ENALBED=0 is required for the code to run properly when copied alpine
RUN CGO_ENABLED=0 GOOS=linux go build -v -mod mod -ldflags "-s -w" -o restapi .

#Now build the runtime container, just a stripped down linux and copy the
#binary to it.
FROM alpine:latest
WORKDIR /app
COPY --from=buildContainer /go/src/app/restapi .

ENV GIN_MODE release

ENV HOST 0.0.0.0
ENV PORT 8080
EXPOSE 8080

CMD ["./restapi"]

我尝试了在Google上找到的不同的Dockerfile,也试着从头开始创建我的自己的Dockerfile。

1个回答

5
你需要绑定容器内的公共网络接口。因为每个容器都是自己的主机,如果你在内部绑定环回接口,它将无法被外界访问。
router.Run("0.0.0.0:8080")

此外,在运行容器时,请确保发布此端口。
docker run --publish 8080:8080 myapp

您在环境变量方面的意图是正确的,但是它们没有在您的代码中使用。

ENV HOST 0.0.0.0
ENV PORT 8080

你可以使用 os.Getenvos.LookupEnv 从你的代码中获取这些变量并使用它们。

1
容器正常运行,但我仍然无法访问0.0.0.0:8080或localhost:8080。 - Mantle LoL
1
我使用了"docker run --publish 8080:8080 hello"来启动容器。并将router.Run("localhost:8080")更改为router.Run("0.0.0.0:8080"),它仍然可以在本地正常运行但无法在容器中访问。 - Mantle LoL
@MantleLoL,你也可以尝试将GIN_MODE设置为debug并查看是否获得更多信息。 - The Fool
我回退到了一个旧的dockerfile,遵循官方文档https://docs.docker.com/language/golang/build-images/。这之前是不起作用的。但是根据您的建议,将我的应用程序源代码中的localhost:8080更改为0.0.0.0:8080解决了问题。看起来像是一个dockerfile +源代码的问题。谢谢。 - Mantle LoL
@MantleLoL,如果你按照下面的描述使用distroless,那么这是一个好方法。我也使用它们来运行我的Go二进制文件。我通常使用gcr.io/distroless/static:nonroot。但是这时你需要运行go build以禁用cgo。 - The Fool
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接