diff --git a/CHANGELOG.md b/CHANGELOG.md index abc0db5..76f3518 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Latest +- [Feature] Multiple platforms on a single server \o/ - [Feature] Easily configure web proxy on the host - [Bugfix] Fix `images pull all` command which failed on "all" image - [Improvement] Add configurable mongodb, SMTP and rabbitmq authentication diff --git a/docs/local.rst b/docs/local.rst index 49005b9..e021f22 100644 --- a/docs/local.rst +++ b/docs/local.rst @@ -148,6 +148,36 @@ If you have configured your platform to use SSL/TLS certificates for HTTPS acces tutor local https create tutor local https renew +Running multiple Open edX platforms on a single server +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +With Tutor, it is easy to run multiple Open edX instances on a single server. To do so, the following configuration parameters must be different for all platforms: + +- ``TUTOR_ROOT``: so that configuration, environment and data are not mixed up between platforms. +- ``LOCAL_PROJECT_NAME``: the various docker-compose projects cannot share the same name. +- ``NGINX_HTTP_PORT``, ``NGINX_HTTPS_PORT``: ports cannot be shared by two different containers. +- ``LMS_HOST``, ``CMS_HOST``: the different platforms must be accessible from different domain (or subdomain) names. + +In addition, a web proxy must be setup on the host, as described :ref:`above `. + +As an example, here is how to launch two different platforms, with nginx running as a web proxy: + + # platform 1 + export TUTOR_ROOT=~/openedx/site1 + tutor config save --set WEB_PROXY=true --set LOCAL_PROJECT_NAME=tutor_site1 --set NGINX_HTTP_PORT=81 --set NGINX_HTTPS_PORT=481 + tutor local quickstart + sudo ln -s $(tutor config printroot)/env/local/proxy/nginx/openedx.conf /etc/nginx/sites-enabled/site1.conf + + + # platform 2 + export TUTOR_ROOT=~/openedx/site2 + tutor config save --set WEB_PROXY=true --set LOCAL_PROJECT_NAME=tutor_site2 --set NGINX_HTTP_PORT=82 --set NGINX_HTTPS_PORT=482 + tutor local quickstart + sudo ln -s $(tutor config printroot)/env/local/proxy/nginx/openedx.conf /etc/nginx/sites-enabled/site2.conf + +You should then have two different platforms, completely isolated from one another, running on the same server. + + Loading different production settings for ``edx-platform`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index 759ab57..eed0050 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -34,7 +34,7 @@ If you'd rather use a graphical user interface for viewing logs, you are encoura "Cannot start service nginx: driver failed programming external connectivity" ----------------------------------------------------------------------------- -The containerized Nginx needs to listen to ports 80 and 443 on the host. If there is already a webserver, such as Apache or Nginx, running on the host, the nginx container will not be able to start. To solve this issue, check :ref:`web_proxy`. +The containerized Nginx needs to listen to ports 80 and 443 on the host. If there is already a webserver, such as Apache or Nginx, running on the host, the nginx container will not be able to start. To solve this issue, check :ref:`out to setup a web proxy `. Help! The Docker containers are eating all my RAM/CPU/CHEESE ------------------------------------------------------------ diff --git a/tutor/local.py b/tutor/local.py index 9862e3c..c2f6407 100644 --- a/tutor/local.py +++ b/tutor/local.py @@ -22,9 +22,7 @@ from . import ops def local(): pass -@click.command( - help="Configure and run Open edX from scratch" -) +@click.command(help="Configure and run Open edX from scratch") @click.option("-p", "--pullimages", "pullimages_", is_flag=True, help="Update docker images") @opts.root def quickstart(pullimages_, root): @@ -42,16 +40,13 @@ def quickstart(pullimages_, root): click.echo(fmt.title("Starting the platform in detached mode")) start.callback(root, True) -@click.command( - help="Update docker images", -) +@click.command(help="Update docker images") @opts.root def pullimages(root): - docker_compose(root, "pull") + config = tutor_config.load(root) + docker_compose(root, config, "pull") -@click.command( - help="Run all configured Open edX services", -) +@click.command(help="Run all configured Open edX services") @opts.root @click.option("-d", "--detach", is_flag=True, help="Start in daemon mode") def start(root, detach): @@ -59,11 +54,11 @@ def start(root, detach): if detach: command.append("-d") - docker_compose(root, *command) + config = tutor_config.load(root) + docker_compose(root, config, *command) if detach: click.echo(fmt.info("The Open edX platform is now running in detached mode")) - config = tutor_config.load(root) http = "https" if config["ACTIVATE_HTTPS"] else "http" urls = [] if not config["ACTIVATE_HTTPS"] and not config["WEB_PROXY"]: @@ -81,7 +76,8 @@ def start(root, detach): @click.command(help="Stop a running platform",) @opts.root def stop(root): - docker_compose(root, "rm", "--stop", "--force") + config = tutor_config.load(root) + docker_compose(root, config, "rm", "--stop", "--force") @click.command( help="""Restart some components from a running platform. @@ -96,7 +92,8 @@ def restart(root, service): command += ["lms", "cms", "lms_worker", "cms_worker"] elif service != "all": command += [service] - docker_compose(root, *command) + config = tutor_config.load(root) + docker_compose(root, config, *command) @click.command( help="Run a command in one of the containers", @@ -116,7 +113,8 @@ def run(root, service, command, args): run_command.append(command) if args: run_command += args - docker_compose(root, *run_command) + config = tutor_config.load(root) + docker_compose(root, config, *run_command) @click.command( help="Create databases and run database migrations", @@ -134,16 +132,18 @@ def init_mysql(root): if os.path.exists(mysql_data_path): return click.echo(fmt.info("Initializing MySQL database...")) - docker_compose(root, "up", "-d", "mysql") + docker_compose(root, config, "up", "-d", "mysql") while True: click.echo(fmt.info(" waiting for mysql initialization")) + # TODO this is duplicate code with the docker_compose function. We + # should rely on a dedicated function in utils module. logs = subprocess.check_output([ "docker-compose", "-f", tutor_env.pathjoin(root, "local", "docker-compose.yml"), - "--project-name", "tutor_local", "logs", "mysql", + "--project-name", config["LOCAL_PROJECT_NAME"], "logs", "mysql", ]) if b"MySQL init process done. Ready for start up." in logs: click.echo(fmt.info("MySQL database initialized")) - docker_compose(root, "stop", "mysql") + docker_compose(root, config, "stop", "mysql") return sleep(4) @@ -219,7 +219,8 @@ def logs(root, follow, tail, service): if tail is not None: command += ["--tail", str(tail)] command += service - docker_compose(root, *command) + config = tutor_config.load(root) + docker_compose(root, config, *command) @click.command(help="Create an Open edX user and interactively set their password") @opts.root @@ -261,12 +262,13 @@ def portainer(root, port): utils.docker_run(*docker_run) def run_bash(root, service, command): - docker_compose(root, "run", "--rm", service, "bash", "-e", "-c", command) + config = tutor_config.load(root) + docker_compose(root, config, "run", "--rm", service, "bash", "-e", "-c", command) -def docker_compose(root, *command): +def docker_compose(root, config, *command): return utils.docker_compose( "-f", tutor_env.pathjoin(root, "local", "docker-compose.yml"), - "--project-name", "tutor_local", + "--project-name", config["LOCAL_PROJECT_NAME"], *command ) diff --git a/tutor/templates/config-defaults.yml b/tutor/templates/config-defaults.yml index eda8d77..70805f0 100644 --- a/tutor/templates/config-defaults.yml +++ b/tutor/templates/config-defaults.yml @@ -21,6 +21,7 @@ DOCKER_IMAGE_NGINX: "nginx:1.13" DOCKER_IMAGE_RABBITMQ: "rabbitmq:3.6.10" DOCKER_IMAGE_SMTP: "namshi/smtp:latest" DOCKER_REGISTRY: "" +LOCAL_PROJECT_NAME: "tutor_local" ELASTICSEARCH_HOST: "elasticsearch" ELASTICSEARCH_PORT: 9200 MEMCACHED_HOST: "memcached"