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