apachegowebdeployvirtual-hosts

Golang webapp with Apache multiple VirtualHosts


Unfortunately I've not been able to deploy a basic Golang WebApp on production server. After going through many documentation and tutorials I understood that I need to run Golang WebApp as a Daemon.

First things first: the production server is a single IP running Ubuntu 16.04 with Apache based multiple VirtualHosts /etc/apache2/sites-enabled/.

Golang environment vars

# set golang environment vars
export GOROOT=/usr/local/go

# set multiple gopaths seperated by ":"
export GOPATH=/var/www/go_projects/gotest.domain2.com

export PATH=$GOPATH/bin:$GOROOT/bin:$PATH

# set PATH so it includes user's private bin directories
PATH="$HOME/bin:$HOME/.local/bin:$PATH"

Systemd Daemon file

[Unit]
Description=GoTest Webserver

[Service]
Type=simple
WorkingDirectory=/var/www/go_projects/gotest.domain2.com
ExecStart=/var/www/go_projects/gotest.domain2.com/main #binary file

[Install]
WantedBy=multi-user.target

VirtualHost Conf

<VirtualHost *:80>
    ServerName gotest.domain.com
    DocumentRoot /var/www/go_projects/gotest.domain2.com

    <Directory /var/www/go_projects/gotest.domain2.com>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
    </Directory>

Go file

package main

import (
    "fmt"
    "log"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

The program on http://gotest.domain2.com unfortunately is not executed. It rather lists the content of DocumentRoot

Manual run returns

admin@xyz:/var/www/go_projects/gotest.domain2.com$ ./main 
2018/02/18 15:52:58 listen tcp :8080: bind: address already in use

What am I missing or is my deployment approach principally wrong? Cheers!

EDIT: As suggested from Michael Ernst, I tried altering the port/proxy settings and here is the result:

http://gotest.domain2.com leads to 503 Service Unavailable

Following is the outcome of sudo netstat -talpen

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       User       Inode       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      0          16367       1250/sshd       
tcp        0      0 0.0.0.0:25              0.0.0.0:*               LISTEN      0          473536      26340/master    
tcp        0      0 0.0.0.0:143             0.0.0.0:*               LISTEN      0          16604       1417/dovecot    
tcp        0      0 0.0.0.0:2000            0.0.0.0:*               LISTEN      5001       17289       1652/asterisk   
tcp        0     xx.xx.xx.xx:22       xx.xx.xx.xx:6126      ESTABLISHED 0          615988      13025/sshd: admin [
tcp6       0      0 :::22                   :::*                    LISTEN      0          16369       1250/sshd       
tcp6       0      0 :::25                   :::*                    LISTEN      0          473537      26340/master    
tcp6       0      0 :::3306                 :::*                    LISTEN      111        17564       1391/mysqld     
tcp6       0      0 :::143                  :::*                    LISTEN      0          16605       1417/dovecot    
tcp6       0      0 :::80                   :::*                    LISTEN      0          612412      12554/apache2   
tcp6       0      0 xx.xx.xx.xx:80       xx.xx.xx.xx:6128      FIN_WAIT2   0          0           -               
tcp6       0      0 xx.xx.xx.xx:80       xx.xx.xx.xx:6129      ESTABLISHED 33         615029      12561/apache2   

Any idea where the problem lies?


Solution

  • As for configuring apache:

    You need to start the go application and in the apache configuration reverse proxy the request to the port 8080 ( which the go daemon you wrote listens to ). The go application needs to be always running so you might want to boot it at system start. Unlike php, which is called from apache, go should run as binary that is always there.

    As for your port issue:

    Make sure that your application is not yet started and that no other application is listening to port 8080. ( you can use netstat -talpen to see that )

    Edit: Port 8080 is often a http proxy. Is there are proxy or another application running on this point?

    Edit: You can configure your apache like that:

    <VirtualHost *:80> 
      ProxyPreserveHost On
      ProxyRequests Off
      ServerName www.example.com
      ServerAlias example.com
      ProxyPass / http://localhost:8080/
      ProxyPassReverse / http://localhost:8080/
    </VirtualHost>
    

    You might also want to make the port of your go-application configurable so you don't need to re-compile the code in case you need to change the port. Also you can bind to the localhost interface so, in case you don't have a firewall configured, people can only access the go application over apache and not directly talk with the go application

    // define the flag
    port := flags.Int("port", 8080, "port to listen on")
    // parse the flags
    flags.Parse();
    // here you might want to add code to make sure the port is valid.
    // start webserver
    log.Fatal(http.ListenAndServe("localhost:"+strconv.Atoi(*port), nil))