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

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

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

问题

我已经创建了一个简单的API,按照YouTube教程进行操作,在本地完美运行。一旦我将应用程序容器化并运行容器,我无法访问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。

英文:

I've created a simple API following a youtube tutorial that works perfectly locally. Once I containerise the app and run the container, I can't access the API at http://localhost:8080. I'm guessing it has something to do with the port settings I'm using in the dockerfile, but I'm not sure.

main.go file:

package main
import (
&quot;net/http&quot;
&quot;github.com/gin-gonic/gin&quot;
&quot;errors&quot;
)
type phone struct{
ID       string `json:&quot;id&quot;`
Model    string `json:&quot;model&quot;`
Year     string `json:&quot;year&quot;`
Quantity int    `json:&quot;quantity&quot;`
}
var phones = []phone{
{ID: &quot;1&quot;, Model: &quot;iPhone 11&quot;, Year: &quot;2019&quot;, Quantity: 4},
{ID: &quot;2&quot;, Model: &quot;iPhone 6&quot;, Year: &quot;2014&quot;, Quantity: 9},
{ID: &quot;3&quot;, Model: &quot;iPhone X&quot;, Year: &quot;2017&quot;, Quantity: 2},
}
func phoneById(c *gin.Context) {
id := c.Param(&quot;id&quot;)
phone, err := getPhoneById(id)
if err != nil {
c.IndentedJSON(http.StatusNotFound, gin.H{&quot;message&quot;: &quot;Phone not found.&quot;})
return
}
c.IndentedJSON(http.StatusOK, phone)
}
func checkoutPhone(c *gin.Context) {
id, ok := c.GetQuery(&quot;id&quot;)
if !ok {
c.IndentedJSON(http.StatusBadRequest, gin.H{&quot;Message&quot;: &quot;Missing id query paramater&quot;})
return
}
phone, err := getPhoneById(id)
if err != nil {
c.IndentedJSON(http.StatusBadRequest, gin.H{&quot;Message&quot;: &quot;Phone not found&quot;})
return
}
if phone.Quantity &lt;= 0 {
c.IndentedJSON(http.StatusBadRequest, gin.H{&quot;Message&quot;: &quot;Phone not available.&quot;})
return
}
phone.Quantity -= 1
c.IndentedJSON(http.StatusOK, phone)
}
func returnPhone(c *gin.Context) {
id, ok := c.GetQuery(&quot;id&quot;)
if !ok {
c.IndentedJSON(http.StatusBadRequest, gin.H{&quot;Message&quot;: &quot;Missing id query paramater&quot;})
return
}
phone, err := getPhoneById(id)
if err != nil {
c.IndentedJSON(http.StatusBadRequest, gin.H{&quot;Message&quot;: &quot;Phone not found&quot;})
return
}
if phone.Quantity &lt;= 0 {
c.IndentedJSON(http.StatusBadRequest, gin.H{&quot;Message&quot;: &quot;Phone not available.&quot;})
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 &amp;phones[i], nil
}
}
return nil, errors.New(&quot;Phone not found.&quot;)
}
func getPhones(c *gin.Context) {
c.IndentedJSON(http.StatusOK, phones)
}
func createPhone(c *gin.Context) {
var newPhone phone
if err := c.BindJSON(&amp;newPhone); err != nil {
return 
}
phones = append(phones, newPhone)
c.IndentedJSON(http.StatusCreated, newPhone)
}
func main(){
router := gin.Default()
router.GET(&quot;/phones&quot;, getPhones)
router.GET(&quot;/phones/:id&quot;, phoneById)
router.POST(&quot;/phones&quot;, createPhone)
router.PATCH(&quot;/checkout&quot;, checkoutPhone)
router.PATCH(&quot;/return&quot;, returnPhone)
router.Run(&quot;localhost:8080&quot;)
}

and my 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 &quot;-s -w&quot; -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 [&quot;./restapi&quot;]

I've tried different dockerfiles found on Google, and tried creating my own from scratch.

答案1

得分: 4

你需要在容器内部绑定到公共网络接口。因为每个容器都是自己的主机,当你在内部绑定到回环接口时,它将无法被外部访问。

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 从你的代码中获取这些变量并使用它们。

英文:

You need to bind to the public network interface inside the container. Because each container is its own host, and when you bind to the loopback interface inside, it will not be accessible to the outside world.

router.Run(&quot;0.0.0.0:8080&quot;)

Additionally, make sure you publish this port when running the container.

docker run --publish 8080:8080 myapp

You actually indicate the right intend with your environment variables, but they are not used in your code.

ENV HOST 0.0.0.0
ENV PORT 8080

You can use os.Getenv or os.LookupEnv to get those variables from your code and use them.

huangapple
  • 本文由 发表于 2022年4月5日 23:25:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/71754445.html
匿名

发表评论

匿名网友

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

确定