Success - New Multisite Install on Dedicated server using ServerPilot, Nginx and Apache

Here’s what I did to install a successful installation of Discourse for multiple instances on a dedicated server that uses Nginx and Apache.

I decided to create a new topic since my installation seems to be a little unique from what I have seen others having to go through. I am very new to Nginx webservers and thought this might help others who may be struggling to connect the dots. I’m also at a beginner level when it comes to using the terminal to access my server, I still have to look up commands for most of the stuff I want to do, but I am finding it so much faster and powerful vs FTP. I still use FTP for a visual representation to locate files and text editing (I don’t like vi or nano editing in the terminal) I use Coda (on a Mac) as my text editor so I have direct access to the server. Which means I can live edit my files just as one would do in the terminal.

I’m using ServerPilot as the control panel to manage my websites on an Ubuntu 14.04 installation. ServerPilot uses Nginx as a reverse proxy in front of the Apache webserver. At first I was a bit confused with this, and basically what they have done is use Nginx as the hub where website requests come into the Nginx server and get routed to Apache to be executed and then back through Nginx to be displayed. This proved to cause some trouble with my installation as I had to figure out where all the config files and such were located.

I started with this tutorial for installing the first instance of Discourse as a dev version …

https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md

Pay special attention to the TCP/IP ports as that was the first place I stumbled, and I see a lot of other posts in the forums from other people who have done the same. If you leave them at the default “80:80” you may get an error stating that port is already in use.

Change the first half of the ports to a port that is not being used, I used 85

This got me through the installation with no errors, but when I went to the url I setup for the forum nothing was showing up. I could connect to the forum using the IP with the port number. Since the forum doesn’t need Apache, I found the conf file that was directing the site to the Apache server with a local IP. I changed this IP to the forum IP with port number.

The path to that section (if you’re also using ServerPilot)

/etc/nginx-sp/vhosts.d/main.conf
(ServerPilot recommends that you change the name of the main.conf file so it is not overwritten on updates)

Now I had a working instance of Discourse, and here’s what I did to set up the multiple instances.

I started with this tutorial, but I was having trouble understanding how the structure was supposed to be setup

If you scroll down to this section …

https://meta.discourse.org/t/multisite-configuration-with-docker/14084/18

This is where I based my installation from, however I did not use the hook settings in the original post. Instead, I duplicated the app.yml file for each site I was creating (only two sites) and renamed them for site specific use. I’ll use the example - site1 and site2.

So in the /var/discourse/containers the files there are as such …

app.yml
site1.yml
site2.yml

(these are your separate containers, which was a term that I didn’t understand at first)

For each file you need to make sure to change the following areas to be specific to your instance …

## which TCP/IP ports should this container expose?
expose:
  - "127.0.0.1:4000:80"   # fwd host port 80   to container port 80 (http)
  - "2222:22" # fwd host port 2222 to container port 22 (ssh)

Notice I changed the first port on both, otherwise you will get port conflict errors.

  ## TODO: List of comma delimited emails that will be made admin and developer
  ## on initial signup example 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'changeme@site1.com'

change the developer email to the email you will use for the admin account

In the mailserver section that follows, be sure to change any settings that are specific to this site such as the username and password. I ended up using Mandrill so the smtp and port numbers were the same for all instances, but I created two accounts. (one for each website)

This next section to edit is key to you getting a separate database for each site. Be sure to change the ‘yoursite’ to something unique to each site.

## These containers are stateless, all data is stored in /shared
volumes:
  - volume:
      host: /var/discourse/shared/yoursite
      guest: /shared
  - volume:
      host: /var/discourse/shared/yoursite/log/var-log
      guest: /var/log

The next thing to do is make sure you have the Nginx proxy URL (the one that is pointing to Apache) changed to match the port you configured in the TCP/IP section. So using the port I used as an example your file should look like this and as I mentioned above, ServerPilot recommends changing the name of the file if you MUST edit (that’s why they give the do not edit warning)

###############################################################################
# DO NOT EDIT THIS FILE.
#
# Your changes to this file will be overwritten by ServerPilot.
#
# For information on how to customize nginx settings, see
# https://serverpilot.io/community/articles/customize-nginx-settings.html
###############################################################################

# Send all requests to apache.
location / {
    proxy_pass      http://localhost:4000;

Be sure to restart Nginx if you make changes to the .conf file (I forgot to do this) and with ServerPilot the restart command is a little different than I had seen elsewhere, this is what did it for me …

service nginx-sp restart

Then start bootstrapping your containers. (do this for each site your configured) Enter these commands in the terminal

./launcher bootstrap site1
(after that is done)
./launcher start site1

You should now be able to load the URL you have for the forum and see your Discourse forum.

I ended up with one of the sites not recognizing the Admin account when I registered with the developer email, and I found I had a stray character at the end of the URL that caused the email to be different. I then went into the /var/discourse/shared/ directory and deleted the folder matching that site. Then I used ./launcher destroy site2 command followed by ./launcher bootstrap site2 and then ./launcher start site2 I’m not sure a rebuild would work since you want the database to be recreated.

I know I probably over-simplified this is some areas, but from what I have seen in other topics/threads, some people need instructions that are overly simplified :wink:

Please let me know if I left any holes, or there is any need for clarification as long as you understand I can only speak for what worked for me since I barely understand this myself HA!!!

One more thing … does any one see any issue with duplicating the app.yml file to be used in this way? It seems this would work perfectly for updating as well.

Best regards … Pops

9 个赞

Actually, ports <1024 are considered “privileged”, using them without assignment is… not technically wrong, but ugly. Also, you should bind Discourse to the local interface:

expose:
  - "127.0.0.1:4000:80"

Change your nginx config accordingly to proxy_pass http://localhost:4000;

Overall, nice write-up, but do turn your YAML and other config file snippets into proper fenced code blocks (``` on a line by itself before and after the code block). Especially for YAML, indentation is really important and it gets lost in simple quote blocks. :wink:

1 个赞

That makes complete sense, I made the changes suggested to my server and they work perfectly. I also changed them in the above instructions as well as used the code markdown instead of the quote markdown (thanks for pointing that out)

… Pops

1 个赞

@elberet hmmm, something isn’t quite right when changing the ports on the production sites

Changing the ports as you suggested on the app.yml file worked great, but after changing the two additional container files with the appropriate changes I get a 502 bad gateway error.

I made the changes to the exposed port, as well as the proxy_pass, I then rebuilt the the instance using ./launcher rebuild as well as restarted the Nginx server.

Any idea what’s wrong?

… Pops

EDIT: very weird … I was issuing the restart command for Nginx, but when I stopped it, and then started instead of “restart” everything is working fine

You can also stop exporting ports entirely by using web.socketed.template.yml:

4 个赞

I saw that post, but had a difficult time understanding how to implement it in the way my server is set up since Nginx is already installed and running in front of Apache.

It had to do with the last step, the path you referenced for sites-enabled an sites-available doesn’t exist and if I just needed to create them, I didn’t know how the files as a whole were to be set. (just a lack of knowledge on my part)

@PopsRocker Hi Pops!

Like you I’m trying to set up Discourse on DigitalOcean with ServerPilot, already running another website in a ServerPilot app (app named ‘test’). I only need a single Discourse site, so my setup will be simpler than yours.

Along with app#1 (test) hosting a website, I’ve got a 2nd ServerPilot app (named ‘discourse’) set up for purposes of pointing to the Discourse install. This gives me the /etc/nginx-sp/vhosts.d/discourse.conf and /etc/nginx-sp/vhosts.d/discourse.d/main.conf files.

So my question: what modifications did you make to your .conf files to get it to forward requests to discourse? I’ve got Discourse running and can access it from discourse.example.com:8888. What is it I need to do in the nginx conf files so that I can see Discourse at discourse.example.com, without appending the port number?

Thx

Sorry, replied too soon and then found my answer above:

in app.yml:

expose:
  - "127.0.0.1:8888:80" # fwd host port 80 to container port 8888 (http)
  - "2222:22" # fwd host port 22 to container port 2222 (ssh)

in /etc/nginx-sp/vhosts.d/discourse.d/main.conf (renamed to main.custom.conf as suggested):

location / {
    proxy_pass $backend_protocol://$backend_host:8888;
}

I’m still a little confused with this Serverpilot / Discourse setup. When you created your “discourse” app in Serverpilot, what did you put in the apps/discourse/public/ directory? I have my current discourse installation in the /var/discourse directory but am not sure how Serverpilot is suppose to recognize that?

You don’t actually have to put anything in the apps/discourse/public/ directory. Instead, you want requests to that Serverpilot app forwarded to your Discourse install living in /var/.

This is accomplished via the /var/discourse/containers/app.yml and /etc/nginx-sp/vhosts.d/discourse.d/main.custom.conf modifications referenced in my previous post.

1 个赞

Thanks for your help! I was able to get it up and running.

I’m using ServerPilot too. Glad I found this article. I will let you know, how it’s worked up for me.
Thank you.

Thanks for the info on this. I know it has been a while, does anyone happen to know what kind of increase in resources on the system end up being consumed when you have a second one?

Maybe it’s because this post is from 2015 … but now I can not find the nginx-sp directory in my installation (I did it with the docker automatically).

I have all the second.yml completely changed, but I don’t know where it’s the nginx file to do the proxy thing.

(帖子已被作者撤回,若未被标记,将在 24 小时内自动删除)

对于第一个站点,这个路径中不应该包含 /standalone/ 吗?

我怀疑是不是在双容器设置中,对多站点(multisite)和 WordPress + Discourse 的用法产生了混淆?:face_with_raised_eyebrow:

我们在同一台服务器上轻松运行多个 Discourse 应用:每个“域实例”对应一个数据容器和一个应用容器。

此外,对于每个“域实例”,我们运行两个应用容器。这意味着在重建容器时可以实现零停机。

为什么?

因为当“容器 A”运行时,我们重建“容器 B”;待“容器 B”重建完成后,我们等待约 1 分钟,然后重新配置反向代理,将其指向“容器 B”的 Unix 套接字,并执行(以 apache2 为例)service apache2 restart

这意味着在切换容器时几乎可以实现零停机(可能只有几毫秒)。

我一直关注这个帖子,惊叹于许多人如何将一个本质上非常简单的事情变得如此困难(或复杂)。

生产环境的 Discourse 运行在 Docker 容器中;优秀的 Discourse 团队提供了核心模板,使 Discourse 可以通过 TCP/IP 套接字或 Unix 域套接字(每个应用实例)对外暴露。此外,优秀的 meta 团队也提供了核心模板,将数据(PostgreSQL)容器与应用(Rails、JS)容器分开运行。

完全相同的概念适用于任何运行在容器中的 Node.js 应用。这些配置(反向代理到容器)是基本的 Docker 任务,适用于任何运行在容器中的 Web 应用。Discourse 只是其中之一,而且正如我们所知,它是最优秀的之一 :slight_smile:

有时我觉得,meta Discourse 的优秀团队通过他们精心编写的包装脚本,让启动他们的容器变得如此简单,以至于用户未能理解:在反向代理后面运行基于 Docker 的 Web 应用的基本原理其实非常简单。

我们可以运行任意数量的 Discourse 应用作为虚拟主机,这与网页管理员在同一台服务器上运行 20 个“传统 LAMP Web 应用”没有任何区别。唯一的区别在于,虚拟主机指向的不是 PHP 文件目录,而是(在我们的案例中)一个 Unix 域套接字。Docker 通过在容器之间共享核心库和覆盖层,使这一过程非常高效!

此外,由于容器间的网络通信是 Docker 内置功能,因此可以轻松设置 Discourse(作为应用容器)与 PostgreSQL(数据)容器协同工作;meta 团队为所有用户提供了示例模板,供那些希望以更多“有趣”方式(配置)运行 Discourse 的用户使用。

我的建议,尽管微不足道,是理解 Discourse 本质上只是一个运行在容器中的 Web 应用,并通过反向代理对外提供服务,就像任何其他 Web 应用一样。我们同样以这种方式运行我们的私有 Docker 注册表(实际上不止一个):一个 Docker 应用通过同一个反向代理暴露到 5000 端口(Docker 注册表的默认端口)。

Discourse 为了简化用户生活(并使 Discourse 对非系统管理员专家也可用),在其开箱即用(OOTB)配置中提供了直接暴露 Web 应用(无需反向代理)的功能。此外,在 Docker 容器外部设置反向代理对所有这些 Docker 化 Web 应用都是一样的。meta 团队也提供了这些模板(想想看,这是多么棒的支持!)。

最后,在反向代理后面运行一个、二十个或更多 Docker 化应用非常容易。它们只是容器(其中每一个都可以是不同的 Discourse 论坛),如何暴露这些容器的选择权在于用户(系统管理员),持久化存储卷(包括数据库)的设置方式也是如此。

我认为这非常简单,meta 团队也让每个人都觉得很简单;但其中一个副作用是,由于过于简单,人们忘记了(或没有学习到)Docker 化配置(在反向代理后面)实际上是多么简单和优雅(与 Discourse 无关)。

希望这能有所帮助。

祝好!

7 个赞

这其实非常简单……

每个应用都有自己独立的共享卷,Unix 域套接字就运行在该卷中(所有其他持久化数据,如上传文件等,也存储在这里)。

你无需直接使用 docker-compose,也不需要特殊的 pup 指令,更无需更改任何配置(只需正确设置容器的基本 yml 文件,然后运行 launcher 即可)。

如果你的 Discourse 站点/应用名为 “farmer_forums”,那么你的 Unix 套接字默认位置例如为:

/var/discourse/shared/farmer_forums/nginx.http.socket

如果你的下一个 Discourse 站点/应用名为 “race_car_forums”,那么你的 Unix 套接字位置例如为:

/var/discourse/shared/race_car_forums/nginx.http.socket

你可以根据需要为成百上千个论坛这样做;每个论坛只需在反向代理中添加一个指向该套接字的条目即可。只需正确命名你的容器 yml 文件。

每个论坛也会有自己独立的数据容器。你可以随意命名这些容器,例如:

  • farmer_forums_data
  • race_car_forums_data

当然,每个数据容器也会有自己独立的共享卷,用于存放其数据库。

你只需要创建一个可正常工作的“双容器解决方案”,然后在反向代理后复制该方案即可。无论是创建一个站点还是 100 个站点,只要遵循这个简单方法,操作都是一样的。

附注:我听说(读过)有些人尝试为多个论坛共用一个数据库容器。就我个人而言,我绝不推荐这种做法。这种配置会为所有论坛带来单点故障风险,也不符合我“保持简单和愚蠢”(KISS)的原则。不过,我在系统管理中喜欢 KISS,因为几十年来我多次因“手滑”犯错 :),所以我不希望因失误而引发更多问题(毕竟我们所有人都会时不时犯错)。

因此,如果你在这方面遇到困难,只需先搭建一个包含应用容器和数据容器的双容器站点,并将其置于反向代理之后。

然后,根据需要将该方案复制多次,每个站点都配备一个应用容器和一个数据容器。所有容器名称必须唯一,并应遵循某种易于记忆的命名规范,例如上面提到的 farmer 和 race car 示例。

我并非有意重复或说教,但以这种方式在反向代理后搭建多个 Discourse 站点几乎毫不费力。

希望这段补充说明能在某些方面对你有所帮助。

简而言之,对我来说,“多站点” simply 意味着多次重复执行“双容器”方案(次数由你决定)置于反向代理之后!:slight_smile: 无论是 1 个还是 10 个站点,基本配置都是一样的,只是容器名称不同,并且需要遵循某种“易于理解”的容器命名规范(如上述两个示例)。Docker 会自动在镜像之间合理共享库文件和覆盖层,因此“无需额外操作”,因为“这正是 Docker 所做的”(自动设置所有共享库、构建 Docker 网络等),这使得该方案“高效”,得益于 Docker 在镜像间共享库等机制。

8 个赞

也许我们应该给这个设置起个别的名字,而不是“多站点”(在我脑海中,“多站点”指的是共享数据容器,正如去年早些时候 sam 在已关闭的教程主题中所提到的那样)。
平行站点?:crazy_face: 我不确定……
如果我没记错的话,既然你为每个数据/网站容器对都有一个 .yml 文件,那么你可以为每个域名指定不同的插件集和 SMTP 设置吗?

1 个赞