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?
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))