"localhost didn't send any data" 当使用 Golang + Gin + Docker

"localhost didn't send any data" when using Golang + Gin + Docker

我根据 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,并尝试从头开始创建我自己的。

您需要绑定到容器内的 public 网络接口。因为每个容器都是自己的宿主机,当你绑定到里面的loopback接口后,外界就无法访问了。

router.Run("0.0.0.0:8080")

此外,请确保在 运行 容器时发布此端口。

docker run --publish 8080:8080 myapp

您实际上用您的环境变量表明了正确的意图,但它们没有在您的代码中使用。

ENV HOST 0.0.0.0
ENV PORT 8080

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