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.

(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)

最初のサイトの場合、このパスには /standalone/ が含まれているはずです、ね?

マルチサイトと、デュアルコンテナ構成における WP+Discourse の間で、何か混同があるのではないかと思うのですが?:face_with_raised_eyebrow:

私たちは、同じサーバー上で複数の Discourse アプリを簡単に実行しています。データ用のコンテナ 1 つと、各「ドメインインスタンス」ごとにアプリ用のコンテナ 1 つを用意しています。

さらに、各「ドメインインスタンス」に対して、2 つのアプリケーションコンテナを実行しています。これにより、コンテナを再構築する際のダウンタイムはゼロになります。

なぜでしょうか?

「コンテナ A」が実行されている間に「コンテナ B」を再構築し、「コンテナ B」の再構築が完了したら約 1 分待ってから、リバースプロキシを再設定して「コンテナ B」の Unix ソケットへプロキシするように切り替えます(Apache2 の場合) service apache2 restart を実行します。

これにより、コンテナを切り替える際のダウンタイムは事実上ゼロ(数ミリ秒程度)になります。

このスレッドを追うにつれて、非常にシンプルであるはずのことが、多くの人にとってどれほど困難(または複雑)に感じられているかに驚かされます。

本番環境の Discourse は Docker コンテナ内で実行されます。また、優れた Discourse チームは、Discourse を TCP/IP ソケットまたは Unix ドメインソケット(アプリケーションインスタンスごと)で公開するためのコアテンプレートを提供しています。さらに、優れた meta チームは、データ(PostgreSQL)コンテナをアプリケーション(Rails、JS)コンテナから分離して実行するためのコアテンプレートも提供しています。

この正確な概念は、コンテナ内で実行される任意の Node.js アプリケーションにも適用されます。これらの設定(コンテナへのリバースプロキシ)は、コンテナ内で実行されるすべての Web アプリケーションに適用される基本的な Docker タスクです。Discourse はその一例に過ぎず、ご存知の通り最も優れたものの一つです :slight_smile:

時々、meta の Discourse チームが、よく作られたラッパースクリプトを使ってコンテナの起動を非常に簡単にしているため、ユーザーがリバースプロキシの背後で Web ベースの Docker アプリケーションを実行する際の基本的な原理が実は非常にシンプルであることを理解していないのではないかと思うことがあります。

私たちは、ウェブマスターが同じサーバーで 20 の「昔ながらの LAMP Web アプリ」を実行するのと同様に、好きなだけ「Discourse アプリケーション」を仮想ホストとして実行できます。唯一の違いは、PHP ファイルのディレクトリへの仮想ホストではなく、(私たちの場合)Unix ドメインソケットへの仮想ホストになることです。Docker は、コンテナ間でコアライブラリとオーバーレイを共有することで、これを非常に効率的に実現します!

さらに、コンテナ間のネットワーク設定は Docker に組み込まれているため、アプリケーションコンテナとしての Discourse を、データコンテナである PostgreSQL と連携させることが非常に簡単です。これは、meta チームがすべてのユーザーに提供する例テンプレートであり、Discourse をより「興味深い」方法(構成)で実行したいと考えている人向けです。

私のアドバイス(もし少しでも価値があれば)は、Discourse は単にリバースプロキシの背後で実行されている Web アプリであり、他の Web アプリと同じであるということを理解することです。私たちは、プライベート Docker レジストリ(実際には複数)も同様に、同じリバースプロキシをポート 5000(デフォルトの Docker レジストリポート)に設定して実行しています。

Discourse は、ユーザーの生活を容易にし(そしてシステム管理者の専門家ではない人々にも Discourse をアクセス可能にする)、その知恵により、OOTB(Out of the Box)構成で Web アプリケーションをリバースプロキシなしで直接公開する機能を提供しています。さらに、Docker コンテナの外でリバースプロキシを設定する方法は、これらすべての Docker 化された Web アプリケーションで同じです。meta チームはこれらのテンプレートも提供しています(考えてみると素晴らしいサポートですね!)。

最後に、リバースプロキシの背後で 1 つ、20 個、あるいはそれ以上の Docker 化されたアプリケーションを実行するのは非常に簡単です。それらは単なるコンテナであり(すべてが異なる Discourse フォーラムであっても構いません)、コンテナをどのように公開するかはユーザー(システム管理者)の選択に委ねられており、データベースを含む永続ストレージボリュームの設定方法も同様です。

これは非常にシンプルであり、meta チームはすべての人のためにそれをシンプルにしました。しかし、それをシンプルにする副作用の一つとして、人々は(または学ばない)Docker 化された構成(リバースプロキシの背後)が実際にはどれほど簡単でエレガントであるかという基本(Discourse に依存しない)を忘れているようです。

少しでもお役に立てれば幸いです。

すべての皆様に幸あれ!

「いいね!」 7

実はとても簡単です……

各アプリケーションには独自の共有ボリュームがあり、そのボリューム内で Unix ドメインソケットが動作します(また、アップロードなどのすべての永続データもここに保存されます)。

docker-compose を直接操作する必要も、特別な pup ディレクティブを使用する必要も、何も変更する必要もありません(コンテナ用の基本的な yml ファイルを正しく設定し、launcher を実行するだけで十分です)。

Discourse サイト/アプリの1つが “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

もちろん、それぞれにはデータベース用の独自の共有ボリュームが用意されます。

必要なことは、動作する「2コンテナ構成」を1つ作成し、それをリバースプロキシの背後に複製するだけです。1つのサイトを作成しても、100のサイトを作成しても、この簡単な方法に従えばすべて同じです。

PS:いくつかのフォーラムで1つのデータベースコンテナを使用しようとする人がいると聞きました(読みました)。個人的には決して推奨しません。その構成はすべてのフォーラムにとって単一の障害点となり、私の好きな「シンプルでバカらしい(KISS)」やり方ではありません。しかし、システム管理においては KISS を好みます。なぜなら、過去数十年で何度も指が太すぎて誤操作をしてしまったからです :)。そのため、ミスをした際(誰もが時々やってしまいます)に、余計なものを壊したくないのです。

したがって、もしこれで苦労しているなら、まず1つのサイトを、アプリとデータコンテナの2つのコンテナ構成で、リバースプロキシの背後に立ち上げてください。

その後、各サイト用にアプリとデータコンテナを用意し、好きなだけそれを複製するだけです。すべてのコンテナ名は一意である必要があり、上記のファーマーやレーシングカーのサンプルのように、覚えやすい命名規則に従うべきです。

繰り返したり説教したりするつもりはありませんが、この方法でリバースプロキシの背後に多くの Discourse サイトを設定するのは、ほぼ自明です。

この追加の説明が、少しでもお役に立てば幸いです。

要するに、私にとって「マルチサイト」とは、リバースプロキシの背後で「2コンテナ」構成を、好きなだけ繰り返すことを意味します :slight_smile: 1つでも10個でも、基本的には設定は同じで、異なるのはコンテナの名前だけです。また、「わかりやすい」コンテナ命名構成(上記の2つの例のように)が存在します。Docker はイメージ間でライブラリやオーバーレイを適切に共有するため、「ここには何もする必要がありません」。なぜなら「それが Docker のやること」だからです(共有ライブラリの設定、Docker ネットワークの構築など、すべて自動で行われます)。これにより、Docker がイメージ間でライブラリを共有する方法のおかげで「効率的」になります。

「いいね!」 8

もしかして、この設定を「マルチサイト」と呼ぶのはやめたほうがいいかもしれません(私が思うに、昨年サムが閉鎖したハウツートピックにあるように、マルチサイトはデータコンテナを共有するものです)。
「並列サイト」なんてどうでしょうか?:crazy_face: 分かりませんけど…
もし私の記憶が正しければ、データ/ウェブのコンテナの組み合わせごとに.ymlファイルがあるなら、ドメインごとに異なるプラグインセットやSMTPを指定できるはずです。

「いいね!」 1