具有 Apache 多个虚拟主机的 Golang Web 应用程序

Golang webapp with Apache multiple VirtualHosts

不幸的是,我无法在生产服务器上部署基本的 Golang WebApp。在阅读了许多文档和教程后,我明白我需要 运行 Golang WebApp 作为守护进程。

首先要做的事情是:生产服务器是单个 IP 运行ning Ubuntu 16.04,带有基于 Apache 的多个 VirtualHosts /etc/apache2/sites-enabled/.

Golang 环境变量

# 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 守护程序文件

[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 *: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>

转到文件

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

不幸的是http://gotest.domain2.com上的程序没有执行。它列出了 DocumentRoot

的内容

手动 运行 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

我错过了什么或者我的部署方法主要是错误的? 干杯!

编辑: 根据 Michael Ernst 的建议,我尝试更改 port/proxy 设置,结果如下:

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

以下是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   

知道问题出在哪里吗?

关于配置apache:

您需要启动 go 应用程序,并在 apache 配置中将请求反向代理到端口 8080(您编写的 go 守护程序侦听该端口)。 go 应用程序需要始终处于 运行ning 状态,因此您可能希望在系统启动时启动它。与从 apache 调用的 php 不同,go 应该 运行 作为始终存在的二进制文件。

关于你的端口问题:

确保您的应用程序尚未启动,并且没有其他应用程序正在侦听端口 8080。(您可以使用 netstat -talpen 查看)

编辑: 端口 8080 通常是一个 http 代理。在这一点上是否有代理或其他应用程序运行ning?

编辑: 您可以像这样配置您的 Apache:

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

您可能还想使 go-application 的端口可配置,这样您就不需要 re-compile 代码,以防您需要更改端口。你也可以绑定到本地主机接口,这样,如果你没有配置防火墙,人们只能通过 apache 访问 go 应用程序,而不能直接与 go 应用程序对话

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