Add memconfig to launcher: Configure defaults based on available RAM
authorJay Pfaffman <pfaffman@relaxpc.com>
Wed, 20 Apr 2016 15:48:39 +0000 (10:48 -0500)
committerJay Pfaffman <pfaffman@relaxpc.com>
Wed, 20 Apr 2016 15:48:39 +0000 (10:48 -0500)
Creates or modifies config of db_shared_buffers, db_work_mem, and
UNICORN_WORKERS based on available RAM. Parameters not set in config
file (i.e., commented out), are left unchanged and a warning is printed.
Uses samples/standalone_template.yml as basis, but if accepted, I
recommend uncommenting these defaults in standalone.yml and using it
instead.

README.md
launcher
samples/standalone_template.yml [new file with mode: 0644]

index 903b8c50a5cf80f14e49109fb27acd185c62a774..8eab1bd94e7d5fb1312e9024708784f1a6a0a571 100644 (file)
--- a/README.md
+++ b/README.md
@@ -57,6 +57,7 @@ Commands:
     destroy:    Stop and remove a container
     enter:      Use docker exec to enter a container
     logs:       Docker logs for container
+       memconfig:  Configure sane defaults for available RAM
     bootstrap:  Bootstrap a container for the config based on a template
     rebuild:    Rebuild a container (destroy old, bootstrap, start new)
 ```
index ed91e797fbcb28c6cc9a3402339f8616ca372a09..08d1be3075f14adba0aa593ec20987bd3817b506 100755 (executable)
--- a/launcher
+++ b/launcher
@@ -24,7 +24,8 @@ cidbootstrap=cids/"$config"_bootstrap.cid
 local_discourse=local_discourse
 image=discourse/discourse:1.0.17
 docker_path=`which docker.io || which docker`
-
+template_path=samples/standalone_template.yml
+changelog=/tmp/changelog # used to test whether sed did anything
 
 if [ "${SUPERVISED}" = "true" ]; then
   restart_policy="--restart=no"
@@ -61,6 +62,7 @@ usage () {
   echo "    logs:       Docker logs for container"
   echo "    bootstrap:  Bootstrap a container for the config based on a template"
   echo "    rebuild:    Rebuild a container (destroy old, bootstrap, start new)"
+  echo "    memconfig:  Configure defaults based on available RAM"
   echo "    cleanup:    Remove all containers that have stopped for > 24 hours"
   echo
   echo "Options:"
@@ -395,7 +397,7 @@ RUBY
   usage
 }
 
-if [ ! -e $config_file ]
+if [[ ! -e $config_file &&  $command -ne "memconfig" ]]
   then
     echo "Config file was not found, ensure $config_file exists"
     echo ""
@@ -458,6 +460,78 @@ set_boot_command() {
   fi
 }
 
+run_memconfig(){
+  if [ "$opt" != "--skip-prereqs" ] ; then
+    check_resources
+  fi
+  if [ -f $config_file ]
+  then
+    cp $config_file $config_file.bak
+    echo "Saving $config_file as $config_file.bak"
+  else
+    echo "Creating $config_file from $template_path"
+    if [ ! -f $template_path ]
+    then
+      echo "$template_path is missing. Exiting."
+      exit 1
+    fi
+    cp $template_path $config_file
+  fi
+
+  # get free mem
+  avail_mem="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')"
+  avail_gb=`expr $(($avail_mem / 950))`
+  avail_cores=`grep -c processor /proc/cpuinfo`
+  echo "Found $avail_mem (${avail_gb}GB), of memory and $avail_cores cores."
+
+  # set db_shared_buffers: "128MB" (1GB) or 256MB * GB
+  if [ "$avail_gb" -eq "1" ]
+  then
+    db_shared_buffers="128"
+  else
+    db_shared_buffers=`expr $avail_gb \* 256`
+  fi
+  echo -e "Setting db_shared_buffers to ${db_shared_buffers}GB\c"
+  sed -i -e "s/^  db_shared_buffers:.*/  db_shared_buffers: \"${db_shared_buffers}GB\"/w $changelog" $config_file
+  if [ -s $changelog ]
+  then
+    echo " successfully."
+    rm $changelog
+  else
+    echo -e ". . . oops!\n---> db_shared_buffers not found in $config_file. Retaining defaults."
+  fi
+
+  # set db_work_mem: "10MB" * GB?
+  db_work_mem=`expr $avail_gb \* 10`
+  echo -e "Setting db_work_mem to $db_work_mem MB\c"
+  sudo sed -i -e "s/^  db_work_mem:.*/  db_work_mem: \"${db_work_mem}MB\"/w $changelog" $config_file
+  if [ -s $changelog ]
+  then
+    echo " successfully."
+    rm $changelog
+  else
+    echo -e ". . . oops!\n---> db_work_mem not found in $config_file. Retaining defaults."
+  fi
+
+  # set UNICORN_WORKERS: 2*GB or 2*cores (the same on DO)
+  if [ "$avail_gb" -le "2" ]
+  then
+    unicorn_workers=`expr $avail_gb \* 2`
+  else
+    unicorn_workers=`expr $avail_cores \* 2`
+  fi
+
+  echo -e "Setting UNICORN_WORKERS to $unicorn_workers\c"
+  sed -i -e "s/^  UNICORN_WORKERS:.*/  UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $config_file
+  if [ -s $changelog ]
+  then
+      echo " successfully."
+      rm $changelog
+  else
+    echo -e ". . . oops!\n---> UNICORN_WORKERS not found in $config_file. Retaining defaults.\n"
+  fi
+}
+
 run_start(){
 
    existing=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
@@ -638,6 +712,11 @@ case "$command" in
       exit 0
       ;;
 
+  memconfig)
+      run_memconfig
+      exit 0
+      ;;
+
   rebuild)
       if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
         echo "Ensuring discourse docker is up to date"
diff --git a/samples/standalone_template.yml b/samples/standalone_template.yml
new file mode 100644 (file)
index 0000000..255076b
--- /dev/null
@@ -0,0 +1,117 @@
+##
+## After making changes to this file, you MUST rebuild for any changes
+## to take effect in your live Discourse instance:
+##
+## /var/discourse/launcher rebuild app
+##
+## Make sure to obey YAML syntax! You can use this site to help check:
+## http://www.yamllint.com/
+
+## this is the all-in-one, standalone Discourse Docker container template
+
+# You may add rate limiting by uncommenting the web.ratelimited template.
+# Out of the box it allows 12 reqs a second per ip, and 100 per minute per ip
+# This is configurable by amending the params in this file
+
+templates:
+  - "templates/postgres.template.yml"
+  - "templates/redis.template.yml"
+  - "templates/web.template.yml"
+  - "templates/web.ratelimited.template.yml"
+
+ ## Comment out the following lines if you wish to add Lets Encrypt for your Discourse install
+ # - "templates/web.ssl.template.yml"
+ # - "templates/web.letsencrypt.ssl.template.yml"
+
+## which TCP/IP ports should this container expose?
+expose:
+  - "80:80"   # fwd host port 80   to container port 80 (http)
+  - "443:443" # fwd host port 443   to container port 443 (https) (ssl ready)
+# If you want Discourse to share a port with another webserver like Apache or nginx,
+# see https://meta.discourse.org/t/17247 for instructions.
+
+# any extra arguments for Docker?
+# docker_args:
+
+params:
+  db_default_text_search_config: "pg_catalog.english"
+
+  ## Set db_shared_buffers to a max of 25% of the total memory.
+  ##
+  ## On 1GB installs set to 128MB (to leave room for other processes)
+  ## on a 4GB instance you may raise to 1GB
+  db_shared_buffers: "128MB"
+  #
+
+  ## Set higher on large instances it defaults to 10MB, for a 3GB install 40MB is a good default
+  ## this improves sorting performance, but adds memory usage per-connection
+  db_work_mem: "10MB"
+  #
+  ## Which Git revision should this container use? (default: tests-passed)
+  #version: tests-passed
+
+env:
+  LANG: en_US.UTF-8
+  # DISCOURSE_DEFAULT_LOCALE: en
+
+  ## TODO: How many concurrent web requests are supported?
+  ## With 2GB we recommend 3-4 workers, with 1GB only 2
+  ## If you have lots of memory, use one or two workers per logical CPU core
+  UNICORN_WORKERS: 3
+
+  ## 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: 'me@example.com'
+
+  ## TODO: The domain name this Discourse instance will respond to
+  DISCOURSE_HOSTNAME: 'discourse.example.com'
+  ## TODO: Uncomment if you want the container to be started with the same
+  ##       hostname (-h option) as specified above (default "$hostname-$config")
+  ## NOTE: 'true' is the only valid value here, any other will be ignored
+  #DOCKER_USE_HOSTNAME: true
+
+  ## TODO: The mailserver this Discourse instance will use
+  DISCOURSE_SMTP_ADDRESS: smtp.example.com         # (mandatory)
+  #DISCOURSE_SMTP_PORT: 587                        # (optional)
+  #DISCOURSE_SMTP_USER_NAME: user@example.com      # (optional)
+  #DISCOURSE_SMTP_PASSWORD: pa$$word               # (optional, WARNING the char '#' in pw can cause problems!)
+  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (optional, default true)
+
+  ## The Lets Encrypt email will allow you to register a FREE SSL certificate if you added the Lets Encrypt template, comment it out if you have set this up
+  # LETSENCRYPT_ACCOUNT_EMAIL: email@awesomedomain.com
+
+  ## The CDN address for this Discourse instance (configured to pull)
+  #DISCOURSE_CDN_URL: //discourse-cdn.example.com
+
+## These containers are stateless, all data is stored in /shared
+volumes:
+  - volume:
+      host: /var/discourse/shared/standalone
+      guest: /shared
+  - volume:
+      host: /var/discourse/shared/standalone/log/var-log
+      guest: /var/log
+
+## The docker manager plugin allows you to one-click upgrade Discourse
+## http://discourse.example.com/admin/docker
+hooks:
+  after_code:
+    - exec:
+        cd: $home/plugins
+        cmd:
+          - git clone https://github.com/discourse/docker_manager.git
+
+## Remember, this is YAML syntax - you can only have one block with a name
+run:
+  - exec: echo "Beginning of custom commands"
+
+  ## If you want to set the 'From' email address for your first registration, uncomment and change:
+  #- exec: rails r "SiteSetting.notification_email='info@unconfigured.discourse.org'"
+  ## After getting the first signup email, re-comment the line. It only needs to run once.
+
+  ## If you want to configure password login for root, uncomment and change:
+  ## Use only one of the following lines:
+  #- exec: /usr/sbin/usermod -p 'PASSWORD_HASH' root
+  #- exec: /usr/sbin/usermod -p "$(mkpasswd -m sha-256 'RAW_PASSWORD')" root
+
+  - exec: echo "End of custom commands"