Melkeydev / go-blueprint

Go-blueprint allows users to spin up a quick Go project using a popular framework
MIT License
2.07k stars 141 forks source link

[Feature Request] Adding bind to `0.0.0.0` by default #210

Closed MitchellBerend closed 1 month ago

MitchellBerend commented 1 month ago

Tell us about your feature request

I just ran into an issue where, when running the application in a docker container, the webserver was not responding to requests since it was listening on localhost:8080. Inside a docker container it can only receive traffic when the webserver is listening on 0.0.0.0:8080.

I think with how common it is for applications to run inside containers, this should be the default.

TODO: check if the following frameworks automatically bind to localhost

Disclaimer

H0llyW00dzZ commented 1 month ago

Tell us about your feature request

I just ran into an issue where, when running the application in a docker container, the webserver was not responding to requests since it was listening on localhost:8080. Inside a docker container it can only receive traffic when the webserver is listening on 0.0.0.0:8080.

I think with how common it is for applications to run inside containers, this should be the default.

Disclaimer

  • [x] I agree

I think something wrong in your dockerFile, try like this:

# Use the official Golang image to create a build artifact.
# This is a multi-stage build. This stage is named 'builder'.
FROM golang:1.22.1 as builder

# Set the working directory outside $GOPATH to enable the support for modules.
WORKDIR /app

# Copy the go.mod and go.sum to download all dependencies.
COPY go.mod go.sum ./

# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed.
RUN go mod download

# Copy the source code.
COPY cmd/ cmd/
COPY internal/ internal/

# Build the application.
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /my-project ./cmd/api/main.go

# Use a Docker multi-stage build to create a lean production image.
# https://docs.docker.com/develop/develop-images/multistage-build/
FROM alpine:latest

# Install ca-certificates in case you need to make calls to HTTPS endpoints.
# The scratch image is the most minimal image in Docker. This image is only 5mb and has no shell.
# If you need to debug within the container, you might want to use a different base image.
RUN apk --no-cache add ca-certificates

WORKDIR /root/

# Copy the pre-built binary file from the previous stage.
COPY --from=builder /my-project .

# Expose port 8080 to the outside world.
EXPOSE 8080

# Command to run the executable.
CMD ["./my-project"]

# Remove the source code after the build is complete
RUN rm -rf /app
H0llyW00dzZ commented 1 month ago

[!NOTE] Also, note that the Dockerfile is only used for production-ready environments because it removes the source code after build is complete.

MitchellBerend commented 1 month ago

@H0llyW00dzZ I think your Dockerfile suffers from the same issue. It's mostly about consistency in behavior when setting or not setting the PORT environment variable. If I run the server with a simple go run cmd/api/main.go the application automatically binds to localhost:8080.

If you run the docker image you build the same way (so without -e"port=8080") the server is limited to localhost within the docker network it's running in. This is inconsistent with the way it runs outside a container.

You can try this with the following docker-compose.yml by running it once with the port commented out and with the port set after to see the difference.

version: '3.8'

services:
  backend:
    build: .
    # environment:
    #   PORT: 8080
    ports:
      - "8080:8080"
  psql:
    image: postgres:latest
    environment:
      POSTGRES_DB: test
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
    ports:
      - "5432:5432"
    volumes:
      - psql_volume:/var/lib/postgresql/data

volumes:
  psql_volume:

PS. I don't think you have to delete files from the 1st stage of a build explicitly. This is my Dockerfile for clarity:

FROM golang:1.22.1-bookworm AS builder

WORKDIR /app

# Copy go.mod and go.sum files to the workspace
COPY go.mod go.sum ./

# Download dependencies
RUN go mod download

# Copy the source code into the container
COPY . .

# Build the Go application
RUN GOOS=linux go build -o frontend-data cmd/api/main.go

# Stage 2: Final Stage
FROM gcr.io/distroless/base-debian12

# Copy the executable from the builder stage
COPY --from=builder /app/frontend-data /

EXPOSE 8080

# Command to run the executable
CMD ["/frontend-data"]
H0llyW00dzZ commented 1 month ago

@H0llyW00dzZ I think your Dockerfile suffers from the same issue. It's mostly about consistency in behavior when setting or not setting the PORT environment variable. If I run the server with a simple go run cmd/api/main.go the application automatically binds to localhost:8080.

If you run the docker image you build the same way (so without -e"port=8080") the server is limited to localhost within the docker network it's running in. This is inconsistent with the way it runs outside a container.

You can try this with the following docker-compose.yml by running it once with the port commented out and with the port set after to see the difference.

version: '3.8'

services:
  backend:
    build: .
    # environment:
    #   PORT: 8080
    ports:
      - "8080:8080"
  psql:
    image: postgres:latest
    environment:
      POSTGRES_DB: test
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
    ports:
      - "5432:5432"
    volumes:
      - psql_volume:/var/lib/postgresql/data

volumes:
  psql_volume:

PS. I don't think you have to delete files from the 1st stage of a build explicitly. This is my Dockerfile for clarity:

FROM golang:1.22.1-bookworm AS builder

WORKDIR /app

# Copy go.mod and go.sum files to the workspace
COPY go.mod go.sum ./

# Download dependencies
RUN go mod download

# Copy the source code into the container
COPY . .

# Build the Go application
RUN GOOS=linux go build -o frontend-data cmd/api/main.go

# Stage 2: Final Stage
FROM gcr.io/distroless/base-debian12

# Copy the executable from the builder stage
COPY --from=builder /app/frontend-data /

EXPOSE 8080

# Command to run the executable
CMD ["/frontend-data"]

it works fine on my machine, till I got blocked

image

H0llyW00dzZ commented 1 month ago

logs:

Attaching to backend-1
backend-1  | 2024/04/01 17:38:37 [H0llyW00dzZ Firewall] [INFO] Connected to database: *********
backend-1  | 2024/04/01 17:38:40 [H0llyW00dzZ] [INFO] Starting server on :8080
backend-1  |
backend-1  |  ┌───────────────────────────────────────────────────┐
backend-1  |  │                    H0llyW00dzZ                    │
backend-1  |  │                   Fiber v2.52.4                   │
backend-1  |  │               http://127.0.0.1:8080               │
backend-1  |  │       (bound on host 0.0.0.0 and port 8080)       │
backend-1  |  │                                                   │
backend-1  |  │ Handlers ............ 534 Processes ........... 1 │
backend-1  |  │ Prefork ....... Disabled  PID ................. 1 │
backend-1  |  └───────────────────────────────────────────────────┘
backend-1  |
H0llyW00dzZ commented 1 month ago

@MitchellBerend, it might be a framework issue, because Fiber automatically binds to 0.0.0.0 (IP Magic Hahaha) without any issues.

MitchellBerend commented 1 month ago

Ah there it is, I have only tried this with chi, but I will check the other frameworks in the morning. Maybe this change should only affect the frameworks that automatically bind to localhost. Thanks for your input @H0llyW00dzZ.

H0llyW00dzZ commented 1 month ago

Ah there it is, I have only tried this with chi, but I will check the other frameworks in the morning. Maybe this change should only affect the frameworks that automatically bind to localhost. Thanks for your input @H0llyW00dzZ.

No problem. Because I don't have any issues with binding, even on Kubernetes (k8s) with custom advanced configurations for the cluster.

MitchellBerend commented 1 month ago

For reference, are you only using fiber on these deployments?

H0llyW00dzZ commented 1 month ago

For reference, are you only using fiber on these deployments?

I don't know about others, but for my deployments, I am using Fiber and HTMX.

MitchellBerend commented 1 month ago

Closing this since the default behavior of a standard generated application pulls the port from the .env file. This means the PORT environment variable is required in the subsequent docker-compose.yml.

If you come across this issue, make sure you check if that variable is set.