22349: Run RailsAPI as a standalone Passenger service
authorBrett Smith <brett.smith@curii.com>
Sat, 30 Nov 2024 19:01:26 +0000 (14:01 -0500)
committerBrett Smith <brett.smith@curii.com>
Mon, 2 Dec 2024 17:31:30 +0000 (12:31 -0500)
The primary motivation for this is to be able to run Passenger with the
same version of Ruby that RailsAPI depends on. On RHEL8, the official
Passenger packages depend on the system Ruby 2.5, which is too old for
us to use.

A secondary benefit is that this simplifies installation for everyone by
eliminating the need for a separate Passenger install and nginx
integration.

A tertiary benefit is that the systemd service definition can better
handle some preparation work that we were previously doing in the
postinst script.

Arvados-DCO-1.1-Signed-off-by: Brett Smith <brett.smith@curii.com>

build/rails-package-scripts/postinst.sh
build/rails-package-scripts/prerm.sh
build/run-library.sh
doc/admin/upgrading.html.textile.liquid
doc/install/install-api-server.html.textile.liquid
tools/salt-install/config_examples/multi_host/aws/pillars/logrotate_api.sls
tools/salt-install/config_examples/multi_host/aws/pillars/nginx_api_configuration.sls
tools/salt-install/config_examples/single_host/multiple_hostnames/pillars/logrotate_api.sls
tools/salt-install/config_examples/single_host/multiple_hostnames/pillars/nginx_api_configuration.sls
tools/salt-install/config_examples/single_host/single_hostname/pillars/logrotate_api.sls
tools/salt-install/config_examples/single_host/single_hostname/pillars/nginx_api_configuration.sls

index 28301333d66ecc679d60ca7596f74961c0bf384e..706184f4a7d80e8a135998d8cae5af081071e937 100644 (file)
@@ -26,6 +26,15 @@ NOT_READY_DOC_URL="https://doc.arvados.org/install/install-api-server.html"
 # This will be set to a command path after we install the version we need.
 BUNDLE=
 
+systemd_quote() {
+    if [ $# -ne 1 ]; then
+        echo "error: systemd_quote requires exactly one argument" >&2
+        return 2
+    fi
+    # See systemd.syntax(7) - Use double quotes with backslash escapes
+    echo "$1" | sed -re 's/[\\"]/\\\0/g; s/^/"/; s/$/"/'
+}
+
 report_web_service_warning() {
     local warning="$1"; shift
     cat >&2 <<EOF
@@ -141,7 +150,7 @@ configure_version() {
 
   case "$DISTRO_FAMILY" in
       debian) WWW_OWNER=www-data ;;
-      rhel) WWW_OWNER=nginx ;;
+      rhel) WWW_OWNER="$(id --group --name nginx || true)" ;;
   esac
 
   # Before we do anything else, make sure some directories and files are in place
@@ -182,38 +191,50 @@ configure_version() {
   run_and_report "Running bundle install" "$BUNDLE" install --prefer-local --quiet
   run_and_report "Verifying bundle is complete" "$BUNDLE" exec true
 
-  if [ -z "$WWW_OWNER" ]; then
-    NOT_READY_REASON="there is no web service account to own Arvados configuration"
-    NOT_READY_DOC_URL="https://doc.arvados.org/install/nginx.html"
-  else
-    cat <<EOF
-
-Assumption: $WEB_SERVICE is configured to serve Rails from
-            $RELEASE_PATH
-Assumption: $WEB_SERVICE and passenger run as $WWW_OWNER
+  local passenger="$("$BUNDLE" exec gem contents passenger | grep -E '/(bin|exe)/passenger$' | tail -n1)"
+  if ! [ -x "$passenger" ]; then
+      echo "Error: failed to find \`passenger\` command after installing bundle" >&2
+      return 1
+  fi
+  "$BUNDLE" exec "$passenger-config" build-native-support
+  "$BUNDLE" exec "$passenger-config" install-agent
+  "$BUNDLE" exec "$passenger-config" install-standalone-runtime
 
-EOF
+  echo -n "Creating symlinks to configuration in $CONFIG_PATH ..."
+  setup_confdirs /etc/arvados "$CONFIG_PATH"
+  setup_conffile environments/production.rb environments/production.rb.example \
+      || true
+  # Rails 5.2 does not tolerate dangling symlinks in the initializers
+  # directory, and this one can still be there, left over from a previous
+  # version of the API server package.
+  rm -f $RELEASE_PATH/config/initializers/omniauth.rb
+  echo "... done."
 
-    echo -n "Creating symlinks to configuration in $CONFIG_PATH ..."
-    setup_confdirs /etc/arvados "$CONFIG_PATH"
-    setup_conffile environments/production.rb environments/production.rb.example \
-        || true
-    # Rails 5.2 does not tolerate dangling symlinks in the initializers
-    # directory, and this one can still be there, left over from a previous
-    # version of the API server package.
-    rm -f $RELEASE_PATH/config/initializers/omniauth.rb
-    echo "... done."
-
-    echo -n "Ensuring directory and file permissions ..."
-    # Ensure correct ownership of a few files
-    chown "$WWW_OWNER:" $RELEASE_PATH/config/environment.rb
-    chown "$WWW_OWNER:" $RELEASE_PATH/config.ru
-    chown "$WWW_OWNER:" $RELEASE_PATH/db/structure.sql
-    chown "$WWW_OWNER:" $RELEASE_PATH/Gemfile.lock
-    chown -R "$WWW_OWNER:" $SHARED_PATH/log
-    chmod 644 $SHARED_PATH/log/*
-    echo "... done."
+  echo -n "Extending systemd unit configuration ..."
+  local systemd_group
+  if [ -z "$WWW_OWNER" ]; then
+      systemd_group="%N"
+  else
+      systemd_group="$(systemd_quote "$WWW_OWNER")"
   fi
+  install -d /lib/systemd/system/arvados-railsapi.service.d
+  # The 20 prefix is chosen so most user overrides should come after, which
+  # is what most admins will expect, but there's still space to put drop-ins
+  # earlier.
+  cat >/lib/systemd/system/arvados-railsapi.service.d/20-postinst.conf <<EOF
+[Service]
+ExecStartPre=+/bin/chgrp $systemd_group log tmp
+ExecStartPre=+-/bin/chgrp $systemd_group \${PASSENGER_LOG_FILE}
+ExecStart=
+ExecStart=$(systemd_quote "$BUNDLE") exec $(systemd_quote "$passenger") start --daemonize --pid-file %t/%N/passenger.pid
+ExecStop=
+ExecStop=$(systemd_quote "$BUNDLE") exec $(systemd_quote "$passenger") stop --pid-file %t/%N/passenger.pid
+ExecReload=
+ExecReload=$(systemd_quote "$BUNDLE") exec $(systemd_quote "$passenger-config") reopen-logs
+${WWW_OWNER:+SupplementaryGroups=$WWW_OWNER}
+EOF
+  systemctl daemon-reload
+  echo "... done."
 
   if [ -n "$NOT_READY_REASON" ]; then
       :
@@ -226,13 +247,8 @@ EOF
       NOT_READY_REASON="database setup could not be completed"
   fi
 
-  if [ -n "$WWW_OWNER" ]; then
-    chown -R "$WWW_OWNER:" $RELEASE_PATH/tmp
-    chmod -R 2775 $RELEASE_PATH/tmp
-  fi
-
-  if [ -z "$NOT_READY_REASON" ] && [ -n "$WEB_SERVICE" ]; then
-      systemctl restart "$WEB_SERVICE"
+  if [ -z "$NOT_READY_REASON" ]; then
+      systemctl try-restart arvados-railsapi.service
   fi
 }
 
index 832b1e372150381d34f18ceb1390caff3e0eb35d..af0291f94f721b4c73241b7f56878bc107b542c1 100644 (file)
@@ -14,6 +14,7 @@ remove () {
   rm -rf $RELEASE_PATH/tmp
   rm -rf $RELEASE_PATH/.bundle
   rm -rf $RELEASE_PATH/log
+  rm -rf /lib/systemd/system/arvados-railsapi.service.d
 }
 
 if [ "$1" = 'remove' ]; then
index a0a6e00e4579b6db43a6b8b9f10c966e7b8a8710..aa9837ac39fed4663321202fed844f77ce6ef58e 100755 (executable)
@@ -553,7 +553,6 @@ BEGIN { OFS="\0"; ORS="\0"; }
     fi
     local railsdir="/var/www/${pkgname%-server}/current"
     local -a pos_args=("$srcdir/=$railsdir" "$pkgname" dir "$version")
-    local license_arg="$license_path=$railsdir/$(basename "$license_path")"
     local -a switches=(--after-install "$scripts_dir/postinst"
                        --before-remove "$scripts_dir/prerm"
                        --after-remove "$scripts_dir/postrm")
@@ -570,7 +569,9 @@ BEGIN { OFS="\0"; ORS="\0"; }
     done
     fpm_build "${srcdir}" "${pos_args[@]}" "${switches[@]}" \
               -x "$exclude_root/vendor/cache-*" \
-              -x "$exclude_root/vendor/bundle" "$@" "$license_arg"
+              -x "$exclude_root/vendor/bundle" "$@" \
+              "$license_path=$railsdir/$(basename "$license_path")" \
+              "$srcdir/arvados-railsapi.service=/lib/systemd/system/arvados-railsapi.service"
     rm -rf "$scripts_dir"
 }
 
index e755302e641dadc30a2ad54c4a8df0ea79dc1e80..222b02b9bf33362832da381e60ec79da3494b55c 100644 (file)
@@ -32,6 +32,10 @@ h2(#main). development main
 
 "previous: Upgrading to 3.0.0":#v3_0_0
 
+h3. Rails API server now runs standalone
+
+The Arvados Rails API server now runs from a standalone Passenger server to simplify deployment. Before upgrading, existing deployments should remove the Rails API server from their nginx configuration. e.g., remove the entire @server@ block with @root /var/www/arvados-api/current/public@ from @/etc/nginx/conf.d/arvados-api-and-controller.conf@. If you customized this deployment at all, the "updated install instructions":{{ site.baseurl }}/install/install-api-server.html#railsapi-config explain how to customize the standalone Passenger server.
+
 h2(#v3_0_0). v3.0.0 (2024-11-12)
 
 "previous: Upgrading to 2.7.4":#v2_7_4
index 5125b91bf0ee444cb2e26e234ce252e874add5c4..0f403e5394f63876791ac62e7a0c66e7159caa3a 100644 (file)
@@ -28,9 +28,7 @@ Here is a simplified diagram showing the relationship between the core services.
 h2(#dependencies). Install dependencies
 
 # "Install PostgreSQL":install-postgresql.html
-# "Install Ruby and Bundler":ruby.html
 # "Install nginx":nginx.html
-# "Install Phusion Passenger":https://www.phusionpassenger.com/docs/tutorials/deploy_to_production/installations/oss/ownserver/ruby/nginx/
 
 h2(#database-setup). Set up database
 
@@ -100,7 +98,7 @@ The @Services@ section of the configuration helps Arvados components contact one
 
 h2(#update-nginx). Update nginx configuration
 
-Use a text editor to create a new file @/etc/nginx/conf.d/arvados-api-and-controller.conf@ with the following configuration.  Options that need attention are marked in <span class="userinput">red</span>.
+Use a text editor to create a new file @/etc/nginx/conf.d/arvados-controller.conf@ with the following configuration.  Options that need attention are marked in <span class="userinput">red</span>.
 
 <notextile>
 <pre><code>proxy_http_version 1.1;
@@ -166,32 +164,6 @@ server {
     proxy_set_header      X-Real-IP         $remote_addr;
   }
 }
-
-server {
-  # This configures the Arvados API server.  It is written using Ruby
-  # on Rails and uses the Passenger application server.
-
-  listen <span class="userinput">localhost:8004</span>;
-  server_name localhost-api;
-
-  root /var/www/arvados-api/current/public;
-  index  index.html index.htm index.php;
-
-  passenger_enabled on;
-  passenger_preload_bundler on;
-  passenger_load_shell_envvars off;
-  # The API server needs to find `arvados-server` in $PATH to read the cluster
-  # configuration. If you have installed `arvados-server` in a nonstandard
-  # location, add that directory to the PATH value here.
-  passenger_env_var PATH /usr/bin:/usr/local/bin;
-
-  # This value effectively limits the size of API objects users can
-  # create, especially collections.  If you change this, you should
-  # also ensure the following settings match it:
-  # * `client_max_body_size` in the previous server section
-  # * `API.MaxRequestSize` in config.yml
-  client_max_body_size 128m;
-}
 </code></pre>
 </notextile>
 
@@ -199,7 +171,22 @@ server {
 
 {% include 'install_packages' %}
 
-{% assign arvados_component = 'arvados-controller' %}
+h3(#railsapi-config). Configure Rails API server
+
+By default, the Rails API server is configured to listen on @localhost:8004@, matching the example cluster configuration above. If you need to change this, edit the @arvados-railsapi.service@ definition to redefine the @PASSENGER_ADDRESS@ and @PASSENGER_PORT@ environment variables, like this:
+
+<notextile>
+<pre><code># <span class="userinput">systemctl edit arvados-railsapi.service</span>
+### Editing /etc/systemd/system/arvados-railsapi.service.d/override.conf in your editor
+<span class="userinput">[Service]
+Environment=PASSENGER_ADDRESS=<strong>0.0.0.0</strong>
+Environment=PASSENGER_PORT=<strong>8040</strong>
+</span></code></pre>
+</notextile>
+
+You can similarly define other Passenger settings if desired. The "Passenger Standalone reference":https://www.phusionpassenger.com/library/config/standalone/reference/ documents all the available settings.
+
+{% assign arvados_component = 'arvados-railsapi arvados-controller' %}
 
 {% include 'start_service' %}
 
index 8377c0b23bbd5302a06d1c488980e51dba602657..4512a552e2f6276a3a8afbce6211bc356ad81cd1 100644 (file)
@@ -20,5 +20,5 @@ logrotate:
         - copytruncate
         - sharedscripts
         - postrotate
-        - '  [ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid`'
-        - endscript
\ No newline at end of file
+        - '  systemctl try-reload-or-restart arvados-railsapi.service'
+        - endscript
index e3c6d1fdbd2d864aee3c1acc1e1b08fbff9afb61..41a1ef75e11ff1c060fee7c1b00aea6072a12d7a 100644 (file)
@@ -14,8 +14,8 @@ nginx:
   servers:
     managed:
       arvados_api.conf:
-        enabled: true
-        overwrite: true
+        enabled: false
+        overwrite: false
         config:
           - server:
             - listen: 'localhost:8004'
index 8377c0b23bbd5302a06d1c488980e51dba602657..4512a552e2f6276a3a8afbce6211bc356ad81cd1 100644 (file)
@@ -20,5 +20,5 @@ logrotate:
         - copytruncate
         - sharedscripts
         - postrotate
-        - '  [ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid`'
-        - endscript
\ No newline at end of file
+        - '  systemctl try-reload-or-restart arvados-railsapi.service'
+        - endscript
index e5c18de798145fc9e327aefc49e8c6452e04d7fc..f1f81e99ca7649c7d65195c65169576e18f258b1 100644 (file)
@@ -20,8 +20,8 @@ nginx:
   servers:
     managed:
       arvados_api.conf:
-        enabled: true
-        overwrite: true
+        enabled: false
+        overwrite: false
         config:
           - server:
             - listen: 'api.internal:8004'
index 8377c0b23bbd5302a06d1c488980e51dba602657..4512a552e2f6276a3a8afbce6211bc356ad81cd1 100644 (file)
@@ -20,5 +20,5 @@ logrotate:
         - copytruncate
         - sharedscripts
         - postrotate
-        - '  [ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid`'
-        - endscript
\ No newline at end of file
+        - '  systemctl try-reload-or-restart arvados-railsapi.service'
+        - endscript
index 254595ee74fe6098cc02bcebff60fbec301097d4..37a1be8c176911a425f51a003993f26c910ddc74 100644 (file)
@@ -20,8 +20,8 @@ nginx:
   servers:
     managed:
       arvados_api.conf:
-        enabled: true
-        overwrite: true
+        enabled: false
+        overwrite: false
         config:
           - server:
             - listen: '__IP_INT__:8004'