0

I have created a Go backend server that contains an api. All works well when running locally "go run server". I did however encounter issues when running it in docker. So I created a Dockerfile and run the linux container in networkmode host but can't access the api in the browser. If it's working I should be able to see a json response from http://localhost:8500/status. So I'm thinking I need permissions or add flags or more installation related scripts. I have been testing different flags and ports in docker but can't identify the issue. See code, dockerfile and command below.

Note: When I run the program locally on windows a security warning pops up, perhaps this is related to the issue?

I'm running docker desktop community v 2.2.0.5 stable.

Api code:

import (
    "log"
    "net/http"
    conf "server/conf"

    "github.com/gorilla/mux"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)
var db *gorm.DB

// Middleware used to add correct permissions for all requests
func middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
        next.ServeHTTP(w, r)
    })
}

func StartApi(config conf.Config, _db *gorm.DB) {

log.Println("Start Api")
    db = _db

    router := mux.NewRouter()
    // Create 
    router.HandleFunc("/login", login).Methods("POST", "OPTIONS")
    router.HandleFunc("/network", createNetwork).Methods("POST", "OPTIONS")

    // Read 
    router.HandleFunc("/users/{networkName}", getUsers).Methods("GET", "OPTIONS")
    router.HandleFunc("/streams/{networkName}", getStreams).Methods("GET", "OPTIONS")
    router.HandleFunc("/status", showStatus).Methods("GET", "OPTIONS")

    log.Println("API started at port " + config.Backend.Api_port) // api port is stored in config
    log.Println(http.ListenAndServe(":"+config.Backend.Api_port, middleware(router)))

    log.Println("Api done!")  }

Dockerfile:

# Start from the latest golang base image
FROM golang:latest

WORKDIR /go/src/server

COPY ./src/server .

# Download packages
RUN go get . 

# Compile code
#RUN go build -o server
RUN go install server

# Expose ports
EXPOSE 8500
EXPOSE 8600

#ENTRYPOINT ./server
CMD ["go", "run", "server"]

Docker-compose:

version : '3'

services:
  react:
    build: ./ReactFrontend/
    container_name: ReactFrontend
    tty: true
    ports:
    - 4000:3000

  backend:
    network_mode: host
    build: ./GoBackend/
    container_name: goserver
    ports:
    - 8500:8500
    - 8600:8600

Docker command:

docker run --net=host goserver

Any help is appreciated,

Cheers!

Grebtsew
  • 192
  • 5
  • 13
  • 2
    What kind of Docker setup are you using? (Is it Docker Toolbox?) Can you show the part of your application code that binds to the network socket? Why do you need host networking, or a tty? – David Maze May 02 '20 at 21:12
  • To follow-up @DavidMaze's comment, you'd indeed need to give more information about your Docker setup (as two different approaches are possible under Windows), and on the warning/error/unwanted-behavior you observe. – ErikMD May 02 '20 at 21:28
  • 1
    FYI the three typical issue that can occur in this context (deploying a backend on Windows and browsing `http://localhost…`) were mentioned in this answer: [Not open web page on port 8080 when start tomcat inside Docker](https://stackoverflow.com/a/59549269/9164010). – ErikMD May 02 '20 at 21:31
  • Does this answer your question? [Not open web page on port 8080 when start tomcat inside Docker](https://stackoverflow.com/questions/59547203/not-open-web-page-on-port-8080-when-start-tomcat-inside-docker) – ErikMD May 02 '20 at 21:31
  • (But maybe you are faced with a different kind of issue, so as suggested beforehand it's worth giving more details in your question anyway!) – ErikMD May 02 '20 at 21:34
  • @DavidMaze I'm running docker desktop community v 2.2.0.5 stable. The backend contains api (port 8500), websockets (port 8600) and connects to a mysql database which is running in a different container. I need the host networking for the mysql connection to work, the connection to the database is working. But my frontend can't connect to websockets or send requests to the api. Im not sure I understand which code you want to see. – Grebtsew May 02 '20 at 22:50
  • @ErikMD I could share the docker inspect for the running container If you think that might help? I can't really see any errors, the backend starts successfully, initiates the database, then starts the api. But unfortunately I can't access the api. – Grebtsew May 02 '20 at 22:53
  • @ErikMD regarding your link, absolutely accurate. I'm pretty sure it can't be a publish problem while running host. I'll try find the container IP and connect through and let you know If problem is solved. – Grebtsew May 02 '20 at 23:02
  • 1
    BTW as mentioned by @DavidMaze, using `docker run --net=host` is a bad practice, as it basically hinders the network isolation provided by Docker. I'd suggest instead, that you use a command such as [docker-compose up](https://docs.docker.com/compose/reference/up/). – ErikMD May 03 '20 at 23:45
  • So I didn't solve the issue yesterday but I have narrowed it down to: I can't (for some reason) access exposed ports on containers that run on network-mode= host. Example: See Docker-Compose file above. The frontend works in current state but can't access the backend. If I run frontend as host aswell, I can't access the frontend in browser. – Grebtsew May 03 '20 at 23:46

1 Answers1

0

So i solved it for now by:

As mentioned here: https://docs.docker.com/compose/networking/

Add to docker-compose:

networks:
  default:
    external:
      name: database_default

As mentioned here: Access to mysql container from other container

I can connect to database as < db_container_name >:3306 in backend.

To automate the process I created a .sh script for handling an extra setup config step on container start. However, due to my structure in my config.yml it was hard to update with just "sed" commands so I created a python program for updating all config data. The Dockerfile, docker-compose file, setup.sh and update_config.py file is shown below.

setup.sh:

#!/bin/bash
# Don't remove this!
# This file is used by dockerfile to replace configs

# Replace config on run
python3 update_config.py

# Start program
go run server

Dockerfile:

# Start from the latest golang base image
FROM golang:latest

WORKDIR /go/src/server

COPY ./src/server .

# Install python3 and yml compability
RUN apt-get update && apt-get install -y python3-pip
RUN python3 --version
RUN pip3 install PyYAML

# Download packages
RUN go get . 

# Compile code
#RUN go build -o server
RUN go install server

# Expose ports
EXPOSE 8500
EXPOSE 8600

# ENV
 ENV DB_HOST "mysql:3306"

#CHMOD setup
RUN chmod +x setup.sh

CMD ["./setup.sh"]

Docker-compose

version : '3'

services:
  react:
    build: ./ReactFrontend/
    container_name: ReactFrontend
    tty: true
    ports:
    - 4000:3000

  backend:
    build: ./GoBackend/
    container_name: GoBackend
    environment:
      DB_HOST: mysql:3306 # Name or IP of DB container!
    ports:
    - 8500:8500
    - 8600:8600

networks:
  default:
    external:
      name: database_default

update_config.py:

import yaml
import os

"""
DONT REMOVE
This file is used in the dockerfile!
"""

fname = "/go/src/server/config.yml"

stream = open(fname, 'r')
data = yaml.safe_load(stream)
stream.close()

# Add more updates here!
if os.environ.get('DB_HOST') is not None:
    data["database"]["ip"] = os.environ['DB_HOST']


# Updated data print
print("Updated Data", data)

# Write changes to config
stream = open(fname, 'w')
yaml.dump(data, stream)
stream.close()

Example docker command that now works if we only want container to run:

docker run -p 8500:8500 -p 8600:8600 --net database_default goserver

It is working fine! And we are avoiding using unnecessary host network mode!

Grebtsew
  • 192
  • 5
  • 13