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

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:

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

@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:

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.

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/가 포함되어야 하는 것 아닌가요?

멀티사이트와 듀얼 컨테이너 설정에서의 WP+Discourse 사이에 혼동이 있는 건 아닌지 궁금합니다. :face_with_raised_eyebrow:

저희는 데이터용 컨테이너 하나와 애플리케이션용 컨테이너 하나를 사용하여 여러 Discourse 애플리케이션을 쉽게(같은 서버에서) 실행합니다. 이는 각 "도메인 인스턴스"마다 적용됩니다.

또한, 각 "도메인 인스턴스"마다 두 개의 애플리케이션 컨테이너를 실행합니다. 즉, 컨테이너를 재빌드할 때 다운타임이 전혀 없습니다.

왜 그런가요?

"컨테이너 A"가 실행 중일 때 "컨테이너 B"를 재빌드하고, "컨테이너 B"의 재빌드가 완료되면 약 1분 정도 기다린 뒤 리버스 프록시를 "컨테이너 B"의 UNIX 소켓으로 프록시하도록 재구성하고 (apache2의 경우) service apache2 restart를 실행하기 때문입니다.

즉, 컨테이너를 전환할 때 다운타임이 사실상 0(몇 밀리초 정도)입니다.

저는 이 스레드를 지켜보며, 본질적으로 매우 단순한 것을 많은 사람들이 얼마나 어렵게(또는 복잡하게) 만들고 있는지 경외감을 느낍니다.

프로덕션 환경의 Discourse는 Docker 컨테이너에서 실행되며, 훌륭한 Discourse 팀은 각 애플리케이션 인스턴스에 대해 Discourse를 TCP/IP 소켓 또는 UNIX 도메인 소켓으로 노출하기 위한 핵심 템플릿을 제공했습니다. 또한, 훌륭한 meta 팀은 데이터(PostgreSQL) 컨테이너를 애플리케이션(Rails, JS) 컨테이너와 별도로 실행하기 위한 핵심 템플릿을 제공했습니다.

이 개념은 컨테이너에서 실행되는 모든 node.js 애플리케이션에도 동일하게 적용됩니다. 이러한 구성(컨테이너로의 리버스 프록시)은 컨테이너에서 실행되는 모든 웹 애플리케이션에 적용되는 기본적인 Docker 작업이며, Discourse는 그중 하나일 뿐입니다. 우리 모두 알고 있듯이, Discourse는 그중에서도 가장 우수한 제품 중 하나입니다 :slight_smile:

때로는 meta Discourse 팀이 잘 만들어진 래퍼 스크립트로 컨테이너를 시작하는 것을 너무 쉽게 만들어, 사용자가 리버스 프록시 뒤에서 웹 기반 Docker 애플리케이션을 실행하는 근본적인 원리가 실제로는 꽤 단순하다는 사실을 이해하지 못하는 경우가 있다고 생각합니다.

저희는 웹마스터가 같은 서버에서 20개의 "구식 LAMP 웹 앱"을 실행하는 것과 마찬가지로, 가상 호스트로 거의 원하는 만큼의 "Discourse 애플리케이션"을 실행할 수 있습니다. 유일한 차이점은 PHP 파일 디렉터리로 향하는 가상 호스트 대신 (저희의 경우) UNIX 도메인 소켓으로 향하는 가상 호스트가 있다는 것입니다. Docker는 컨테이너 간에 핵심 라이브러리와 오버레이를 공유함으로써 이를 매우 효율적으로 만들어 줍니다!

또한, 컨테이너 간 네트워킹은 Docker에 내장되어 있기 때문에, PostgreSQL(데이터) 컨테이너와 함께 작동하도록 Discourse(애플리케이션 컨테이너)를 설정하는 것은 매우 쉽습니다. meta 팀은 더 “흥미로운” 방식(구성)으로 Discourse를 실행하는 것을 즐기고 싶은 사용자를 위해 예제 템플릿으로 이를 제공합니다.

제 조언은, 그 가치가 적더라도, Discourse가 리버스 프록시 뒤에서 컨테이너로 실행되는, 다른 모든 웹 앱과 같은 단순한 웹 애플리케이션임을 이해하라는 것입니다. 저희는 또한 프라이빗 Docker 레지스트리(실제로 하나 이상)를 동일한 방식으로 실행합니다. 즉, 동일한 리버스 프록시 뒤에서 포트 5000(기본 Docker 레지스트리 포트)으로 연결되는 Docker 애플리케이션입니다.

Discourse는 사용자가 삶을 쉽게(그리고 시스템 관리 전문가가 아닌 사용자에게 Discourse를 접근 가능하게) 만들기 위해, OOTB(Out-Of-The-Box) 구성에서 웹 애플리케이션을 직접(리버스 프록시 없이) 노출할 수 있는 기능을 제공합니다. 또한, Docker 컨테이너 외부에서 리버스 프록시를 설정하는 것은 이러한 모든 Docker화 된 웹 애플리케이션에 대해 동일합니다. meta 팀은 이러한 템플릿도 제공합니다(생각해 보면 정말 훌륭한 지원이죠!).

마지막으로, 리버스 프록시 뒤에서 하나, 스물, 혹은 그 이상의 Docker화 된 애플리케이션을 실행하는 것은 매우 쉽습니다. 그것들은 그저 컨테이너일 뿐이며(그리고 모두 다른 Discourse 포럼일 수 있습니다) 컨테이너를 노출하는 방식의 선택은 사용자(시스템 관리자)에게 달려 있으며, 데이터베이스를 포함한 영속적 저장 볼륨을 설정하는 방식의 선택도 마찬가지입니다.

저는 이것이 매우 단순하다고 생각하며, meta 팀은 모두를 위해 이를 매우 단순하게 만들어 주었습니다. 하지만 너무 단순하게 만든 것의 부수적 효과 중 하나는, 사람들이 Docker화 된 구성(리버스 프록시 뒤에서)이 실제로 얼마나 쉽고 우아한지(Discourse와 무관하게)를 잊거나(또는 배우지 못하거나) 한다는 것입니다.

이 글이 조금이라도 도움이 되기를 바랍니다.

모두에게 행운을 빕니다!

정말 간단합니다…

각 애플리케이션은 자체 공유 볼륨을 가지며, 이 볼륨은 유닉스 도메인 소켓이 실행되는 곳(업로드 등 모든 영구 데이터가 저장되는 곳)입니다.

docker-compose를 직접 다루거나, 특수한 pup 지시문을 사용하거나, 아무것도 변경할 필요가 없습니다(컨테이너에 대해 기본 yml 파일을 올바르게 설정하고 런처를 실행하는 것만 하면 됩니다).

Discourse 사이트/앱 중 하나가 "farmer_forums"라고 하면, 유닉스 소켓은 기본적으로 다음과 같은 위치에 있습니다:

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

다음 Discourse 사이트/앱이 "race_car_forums"라면, 유닉스 소켓은 (예를 들어) 여기에 있습니다:

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

원하는 경우 수백 개의 포럼에 대해서도 이렇게 할 수 있으며, 각 포럼은 해당 소켓으로의 리버스 프록시 항목일 뿐입니다. 컨테이너 yml 파일 이름을 적절히 지정하기만 하면 됩니다.

각 포럼에는 자체 데이터 컨테이너도 있습니다. 이러한 컨테이너에는 원하는 명명 규칙을 사용할 수 있습니다. 예를 들어:

  • farmer_forums_data
  • race_car_forums_data

물론, 각 데이터 컨테이너에는 데이터베이스를 위한 자체 공유 볼륨이 있습니다.

해야 할 일은 하나의 작동하는 "2컨테이너 솔루션"을 만든 후 리버스 프록시 뒤에 이를 복제하는 것입니다. 이 간단한 방법을 따르다면 한 사이트든 100개 사이트든 모두 동일합니다.

P.S.: 여러 다른 포럼을 위한 단일 데이터베이스 컨테이너를 시도하는 사람들을 보고(읽고) 들었습니다. 개인적으로는 이를 절대 권장하지 않습니다. 해당 구성은 모든 포럼에 대한 단일 장애점을 생성하며, 제가 일을 처리하는 방식이 아닙니다(“간단하고 바보처럼” 유지하기, 즉 KISS 원칙). 하지만 시스템 관리에서 KISS 원칙을 좋아하는 이유는, 수십 년간 실수로 인해 여러 번 문제를 일으켰기 때문입니다 :slight_smile: 실수(우리는 모두 가끔 실수를 합니다)를 했을 때, 필요 이상으로 많은 것을 망가뜨리고 싶지 않기 때문입니다.

따라서, 이 문제로 고생하고 있다면; 리버스 프록시 뒤에 애플리케이션 컨테이너와 데이터 컨테이너 두 개로 구성된 한 사이트를 실행되도록만 하세요.

그런 다음, 각 사이트마다 애플리케이션 컨테이너와 데이터 컨테이너를 갖추고 원하는 만큼 복제하세요. 모든 컨테이너 이름은 고유해야 하며, 위의 농부(race car) 및 레이싱 카(race car) 예시와 같이 기억하기 쉬운 명명 규칙을 따르는 것이 좋습니다.

반복적이거나 설교적으로 들리고 싶지는 않지만, 이 방식으로 리버스 프록시 뒤에 여러 Discourse 사이트를 설정하는 것은 거의 자명할 정도로 쉽습니다.

이 추가 설명이 조금이라도 도움이 되기를 바랍니다.

요약하자면, 저에게 "멀티사이트"란 단순히 리버스 프록시 뒤에 “2컨테이너” 솔루션을 원하는 만큼, 여러 번 수행하는 것을 의미합니다! :slight_smile: 하나든 열이든, 기본적으로 구성은 모두 동일하며, 컨테이너 이름만 다르고, (위 두 예시와 같은) “이해하기 쉬운” 컨테이너 명명 구성이 있을 뿐입니다. Docker는 이미지 간에 라이브러리와 오버레이를 적절히 공유하므로, “할 일이 없습니다”. 왜냐하면 “그것이 docker가 하는 일”(모든 공유 라이브러리 설정, docker 네트워크 구축 등)이기 때문입니다. 이는 docker가 이미지 간에 라이브러리를 공유하는 방식 때문에 "효율적"입니다.

이 설정에 multisite(내 생각에는 sam이 작년에 닫은 howto 주제에서 언급된 데이터 컨테이너를 공유하는 것)라는 이름 대신 다른 이름을 붙이는 것이 좋을 것 같아요.
병렬 사이트? :crazy_face: 잘 모르겠네요…
제가 잘못 알고 있는 게 아니라면, 데이터/웹 컨테이너 쌍마다 .yml 파일이 있으므로 도메인별로 다른 플러그인 세트와 smtp를 지정할 수 있는 거죠?