├── pillar ├── top.sls └── imgur-display.sls ├── salt ├── common.sls ├── top.sls ├── ruby-falcon │ ├── ruby-falcon-wrapper │ ├── install.sh │ └── init.sls ├── nginx-passenger │ ├── nginx-logrotate │ ├── passenger-logrotate │ ├── install.sh │ ├── nginx-conf │ ├── nginx-init │ └── init.sls ├── app │ └── imgur-display │ │ ├── vhost │ │ └── init.sls ├── ssh.sls └── deploy-user │ └── init.sls ├── cloudinit-template.sh ├── LICENSE └── README.md /pillar/top.sls: -------------------------------------------------------------------------------- 1 | base: 2 | '*': 3 | - imgur-display 4 | -------------------------------------------------------------------------------- /salt/common.sls: -------------------------------------------------------------------------------- 1 | # Install git 2 | git: 3 | pkg.installed 4 | 5 | # Install NTP 6 | ntp: 7 | pkg.installed 8 | -------------------------------------------------------------------------------- /salt/top.sls: -------------------------------------------------------------------------------- 1 | base: 2 | '*': 3 | - app.imgur-display 4 | - common 5 | - deploy-user 6 | - nginx-passenger 7 | - ruby-falcon 8 | - ssh 9 | -------------------------------------------------------------------------------- /salt/ruby-falcon/ruby-falcon-wrapper: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export RUBY_HEAP_MIN_SLOTS=1250000 3 | export RUBY_GC_MALLOC_LIMIT=100000000 4 | exec "{{ pillar['ruby_location'] }}/bin/ruby" "$@" 5 | -------------------------------------------------------------------------------- /salt/nginx-passenger/nginx-logrotate: -------------------------------------------------------------------------------- 1 | /opt/nginx/logs/*.log { 2 | daily 3 | missingok 4 | compress 5 | rotate 7 6 | dateext 7 | notifempty 8 | sharedscripts 9 | extension gz 10 | copytruncate 11 | } 12 | -------------------------------------------------------------------------------- /salt/nginx-passenger/passenger-logrotate: -------------------------------------------------------------------------------- 1 | /var/www/*/shared/log/*.log { 2 | daily 3 | missingok 4 | compress 5 | rotate 365 6 | dateext 7 | notifempty 8 | sharedscripts 9 | extension gz 10 | copytruncate 11 | } 12 | -------------------------------------------------------------------------------- /pillar/imgur-display.sls: -------------------------------------------------------------------------------- 1 | imgur_display_location: "/var/www/imgur-display" 2 | passenger_max_pool_size: "4" 3 | passenger_max_requests: "2000" 4 | passenger_version: "4.0.8" 5 | ruby_location: "/opt/ruby-1.9.3-p385-falcon-gc" 6 | -------------------------------------------------------------------------------- /salt/ruby-falcon/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /usr/local/src/ 4 | tar xvfz ruby-1.9.3-p385.tar.gz 5 | cd ruby-1.9.3-p385 6 | cat ../p385...p385_falcon_gc.diff | patch -p1 7 | ./configure --prefix={{ pillar['ruby_location'] }} 8 | make 9 | make install 10 | -------------------------------------------------------------------------------- /salt/nginx-passenger/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /usr/local/src/ 4 | tar xvfz nginx-1.4.1.tar.gz 5 | {{ pillar['ruby_location'] }}/bin/passenger-install-nginx-module --auto --prefix=/opt/nginx --nginx-source-dir=/usr/local/src/nginx-1.4.1 --extra-configure-flags="--with-http_gzip_static_module" 6 | -------------------------------------------------------------------------------- /salt/app/imgur-display/vhost: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | server_name imgur-display.local; 4 | 5 | root {{ pillar['imgur_display_location'] }}/current/public; 6 | 7 | access_log {{ pillar['imgur_display_location'] }}/shared/log/nginx-access.log; 8 | error_log {{ pillar['imgur_display_location'] }}/shared/log/nginx-error.log; 9 | 10 | passenger_enabled on; 11 | passenger_user www-data; 12 | passenger_group www-data; 13 | rack_env production; 14 | } 15 | -------------------------------------------------------------------------------- /salt/ssh.sls: -------------------------------------------------------------------------------- 1 | include: 2 | - deploy-user 3 | 4 | # Restart SSH if the configuration changes 5 | ssh: 6 | service: 7 | - running 8 | - enable: True 9 | - watch: 10 | - file: ssh-conf 11 | 12 | # Reconfigure SSH to only allow access using key-based authentication 13 | ssh-conf: 14 | file.sed: 15 | - name: /etc/ssh/sshd_config 16 | - before: "#PasswordAuthentication yes" 17 | - after: "PasswordAuthentication no" 18 | # We only want to make these changes if we can log in as deploy 19 | - require: 20 | - ssh_auth: deploy 21 | -------------------------------------------------------------------------------- /cloudinit-template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | HOSTNAME=hostname_here 3 | SALT_MASTER=internal_ip_here 4 | 5 | echo $HOSTNAME > /etc/hostname 6 | hostname --file /etc/hostname 7 | 8 | sed --in-place -e "s/127.0.0.1 localhost/127.0.0.1 $HOSTNAME localhost/" /etc/hosts 9 | sed --in-place -e "s/::1 ip6-localhost ip6-loopback/::1 $HOSTNAME ip6-localhost ip6-loopback/" /etc/hosts 10 | sed --in-place "2i $SALT_MASTER salt" /etc/hosts 11 | 12 | add-apt-repository -y ppa:saltstack/salt 13 | 14 | apt-get update 15 | apt-get --yes dist-upgrade 16 | 17 | apt-get --yes install salt-minion 18 | reboot 19 | -------------------------------------------------------------------------------- /salt/deploy-user/init.sls: -------------------------------------------------------------------------------- 1 | deploy: 2 | group.present: 3 | - system: False 4 | 5 | # Add the deploy user 6 | user.present: 7 | - fullname: Deploy User 8 | - home: /home/deploy 9 | - shell: /bin/bash 10 | - gid_from_name: True 11 | # Enable sudo access for the deploy user 12 | - groups: 13 | - sudo 14 | - require: 15 | - group: deploy 16 | 17 | # Set up authorized_keys for the deploy user 18 | ssh_auth.present: 19 | - user: deploy 20 | - names: 21 | - ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBC3jdO0ojv6W28wA95qJQexaFNMtVte1xEASeNTAPgyjTqzojZ3cINVXbZS55UD83upMJd5jugohfKp+k/Dus+Y= jlund@Mal 22 | - require: 23 | - user: deploy 24 | -------------------------------------------------------------------------------- /salt/nginx-passenger/nginx-conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes 4; 3 | 4 | events { 5 | worker_connections 1024; 6 | } 7 | 8 | 9 | http { 10 | passenger_root {{ pillar['ruby_location'] }}/lib/ruby/gems/1.9.1/gems/passenger-{{ pillar['passenger_version'] }}; 11 | passenger_ruby /usr/local/bin/ruby-falcon-wrapper; 12 | passenger_max_pool_size {{ pillar['passenger_max_pool_size'] }}; 13 | passenger_max_requests {{ pillar['passenger_max_requests'] }}; 14 | 15 | include mime.types; 16 | default_type application/octet-stream; 17 | 18 | sendfile on; 19 | tcp_nopush on; 20 | client_max_body_size 10M; 21 | 22 | keepalive_timeout 10; 23 | 24 | gzip on; 25 | gzip_types text/css text/xml text/plain application/x-javascript application/atom+xml application/rss+xml; 26 | 27 | 28 | include /etc/nginx/sites-enabled/*; 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Joshua Lund 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | salt-rack 2 | ========= 3 | 4 | Sample Rack application [Salt](http://saltstack.com/) States that will install Nginx, Passenger, Ruby 1.9.3 + the [Falcon patch](https://gist.github.com/funny-falcon/4755042). They also demonstrate how to deploy a [sample Rack application](https://github.com/jlund/imgur-display) using git. 5 | 6 | Specifically, these states do the following: 7 | 8 | * Install a few crucial packages like git and NTP 9 | * Create a deploy user that the application files will belong to 10 | * Add an SSH public key to the deploy user's Authorized Keys file 11 | * Reconfigure OpenSSH to only allow access via SSH keys 12 | * Install Ruby 1.9.3 + the Falcon patch 13 | * Install Bundler 14 | * Install Nginx + Passenger 15 | * Set up and enable an Nginx vhost 16 | * Create all necessary application directories 17 | * Use git to checkout the latest revision of the [imgur-display](https://github.com/jlund/imgur-display) codebase 18 | * Create required symlinks 19 | * Use bundler to install all Gem dependencies 20 | 21 | Running these states will leave you with a fully-functional Rack application server that is ready to show you a random picture from imgur. With some incredibly minor adjustments, these states will deploy your own application! It's my hope that they will be helpful to anyone who needs to set up a similar server using Salt. 22 | 23 | A cloudinit template is also included that you can use to automatically provision Salt on a new Ubuntu server. 24 | 25 | These states were tested on Ubuntu 12.04.2 LTS but should also work on Debian 7. 26 | 27 | Enjoy! 28 | -------------------------------------------------------------------------------- /salt/nginx-passenger/nginx-init: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | ### BEGIN INIT INFO 4 | # Provides: nginx 5 | # Required-Start: $all 6 | # Required-Stop: $all 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: starts the nginx web server 10 | # Description: starts nginx using start-stop-daemon 11 | ### END INIT INFO 12 | 13 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 14 | DAEMON=/opt/nginx/sbin/nginx 15 | NAME=nginx 16 | DESC=nginx 17 | 18 | test -x $DAEMON || exit 0 19 | 20 | # Include nginx defaults if available 21 | if [ -f /etc/default/nginx ] ; then 22 | . /etc/default/nginx 23 | fi 24 | 25 | set -e 26 | 27 | case "$1" in 28 | start) 29 | echo -n "Starting $DESC: " 30 | start-stop-daemon --start --quiet --pidfile /opt/nginx/logs/nginx.pid \ 31 | --exec $DAEMON -- $DAEMON_OPTS || true 32 | echo "Done." 33 | ;; 34 | stop) 35 | echo -n "Stopping $DESC: " 36 | start-stop-daemon --stop --quiet --pidfile /opt/nginx/logs/nginx.pid \ 37 | --exec $DAEMON || true 38 | echo "Done." 39 | ;; 40 | restart|force-reload) 41 | echo -n "Restarting $DESC: " 42 | start-stop-daemon --stop --quiet --pidfile \ 43 | /opt/nginx/logs/nginx.pid --exec $DAEMON || true 44 | sleep 1 45 | start-stop-daemon --start --quiet --pidfile \ 46 | /opt/nginx/logs/nginx.pid --exec $DAEMON -- $DAEMON_OPTS || true 47 | echo "Done." 48 | ;; 49 | reload) 50 | echo -n "Reloading $DESC configuration: " 51 | start-stop-daemon --stop --signal HUP --quiet --pidfile /opt/nginx/logs/nginx.pid \ 52 | --exec $DAEMON || true 53 | echo "Done." 54 | ;; 55 | *) 56 | N=/etc/init.d/$NAME 57 | echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 58 | exit 1 59 | ;; 60 | esac 61 | 62 | exit 0 63 | -------------------------------------------------------------------------------- /salt/ruby-falcon/init.sls: -------------------------------------------------------------------------------- 1 | # Install Ruby dependencies 2 | ruby-dependencies: 3 | pkg.installed: 4 | - pkgs: 5 | - autoconf 6 | - build-essential 7 | - libreadline-dev 8 | - libssl-dev 9 | - libyaml-dev 10 | - zlib1g-dev 11 | 12 | # Download the Falcon patch 13 | ruby-falcon-patch: 14 | file.managed: 15 | - name: /usr/local/src/p385...p385_falcon_gc.diff 16 | - source: https://github.com/funny-falcon/ruby/compare/p385...p385_falcon_gc.diff 17 | - source_hash: sha256=480c8ae3d2c6f24c20efec5a5d5855904f80e7c32fa4cef16a3c752d56b24e78 18 | 19 | # Download the Ruby source code 20 | ruby-source: 21 | file.managed: 22 | - name: /usr/local/src/ruby-1.9.3-p385.tar.gz 23 | - source: http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p385.tar.gz 24 | - source_hash: sha256=4b15df007f5935ec9696d427d8d6265b121d944d237a2342d5beeeba9b8309d0 25 | 26 | # Run the Ruby install script 27 | ruby: 28 | cmd.script: 29 | - name: salt://ruby-falcon/install.sh 30 | - unless: {{ pillar['ruby_location'] }}/bin/ruby -v | grep p385 31 | - template: jinja 32 | - require: 33 | - pkg: ruby-dependencies 34 | - file: ruby-falcon-patch 35 | - file: ruby-source 36 | 37 | # Generate the wrapper script that contains GC settings 38 | /usr/local/bin/ruby-falcon-wrapper: 39 | file.managed: 40 | - source: salt://ruby-falcon/ruby-falcon-wrapper 41 | - template: jinja 42 | - mode: 755 43 | - require: 44 | - cmd: ruby 45 | 46 | # Install Bundler 47 | bundler: 48 | cmd.run: 49 | - name: {{ pillar['ruby_location'] }}/bin/gem install bundler 50 | - unless: {{ pillar['ruby_location'] }}/bin/gem list | grep bundler 51 | - require: 52 | - cmd: ruby 53 | 54 | # Make symlinks 55 | {% for binary in 'bundle', 'erb', 'gem', 'irb', 'rake', 'rdoc', 'ri', 'ruby' %} 56 | /usr/local/bin/{{ binary }}: 57 | file.symlink: 58 | - target: {{ pillar['ruby_location'] }}/bin/{{ binary }} 59 | - require: 60 | - cmd: ruby 61 | {% endfor %} 62 | -------------------------------------------------------------------------------- /salt/app/imgur-display/init.sls: -------------------------------------------------------------------------------- 1 | include: 2 | - deploy-user 3 | - nginx-passenger 4 | - ruby-falcon 5 | 6 | # Generate the imgur-display virtual host 7 | imgur-display-vhost: 8 | file.managed: 9 | - name: /etc/nginx/sites-available/imgur-display 10 | - source: salt://app/imgur-display/vhost 11 | - template: jinja 12 | - require: 13 | - file: /etc/nginx/sites-available 14 | 15 | # Enable the imgur-display virtual host 16 | imgur-display-vhost-symlink: 17 | file.symlink: 18 | - name: /etc/nginx/sites-enabled/imgur-display 19 | - target: /etc/nginx/sites-available/imgur-display 20 | - require: 21 | - file: imgur-display-vhost 22 | - file: /etc/nginx/sites-enabled 23 | 24 | # Create the application directories 25 | {% for directory in 'bundle', 'log' %} 26 | {{ pillar['imgur_display_location'] }}/shared/{{ directory }}: 27 | file.directory: 28 | - user: deploy 29 | - group: deploy 30 | - makedirs: True 31 | - require: 32 | - user: deploy 33 | {% endfor %} 34 | 35 | # Check out the latest revision of the codebase 36 | imgur-display-codebase: 37 | git.latest: 38 | - name: https://github.com/jlund/imgur-display.git 39 | - target: {{ pillar['imgur_display_location'] }}/current 40 | - runas: deploy 41 | - require: 42 | - pkg: git 43 | - user: deploy 44 | - file: {{ pillar['imgur_display_location'] }}/shared/bundle 45 | 46 | # Symlink the log directory to the shared location 47 | imgur-display-log-symlink: 48 | file.symlink: 49 | - name: {{ pillar['imgur_display_location'] }}/current/log 50 | - target: {{ pillar['imgur_display_location'] }}/shared/log 51 | - require: 52 | - git: imgur-display-codebase 53 | - file: {{ pillar['imgur_display_location'] }}/shared/log 54 | 55 | # Install the bundle 56 | bundle-install: 57 | cmd.run: 58 | - name: bundle install --deployment --path {{ pillar['imgur_display_location'] }} 59 | - user: deploy 60 | - cwd: {{ pillar['imgur_display_location'] }}/current 61 | - require: 62 | - cmd: bundler 63 | - file: /usr/local/bin/bundle 64 | - git: imgur-display-codebase 65 | - user: deploy 66 | 67 | # Restart the imgur-display application if the codebase or virtual host change 68 | extend: 69 | nginx: 70 | service: 71 | - watch: 72 | - git: imgur-display-codebase 73 | - file: imgur-display-vhost 74 | -------------------------------------------------------------------------------- /salt/nginx-passenger/init.sls: -------------------------------------------------------------------------------- 1 | include: 2 | - ruby-falcon 3 | 4 | # Install the libcurl OpenSSL development files that Passenger requires 5 | libcurl4-openssl-dev: 6 | pkg.installed 7 | 8 | # Install Passenger 9 | passenger: 10 | cmd.run: 11 | - name: {{ pillar['ruby_location'] }}/bin/gem install passenger -v {{ pillar['passenger_version'] }} 12 | - unless: {{ pillar['ruby_location'] }}/bin/gem list | grep -e passenger -e {{ pillar['passenger_version'] }} 13 | - require: 14 | - cmd: bundler 15 | - pkg: libcurl4-openssl-dev 16 | 17 | # Make Passenger Symlinks 18 | # -- 19 | {% for binary in 'passenger-memory-stats', 'passenger-status' %} 20 | /usr/local/bin/{{ binary }}: 21 | file.symlink: 22 | - target: {{ pillar['ruby_location'] }}/bin/{{ binary }} 23 | - require: 24 | - cmd: passenger 25 | {% endfor %} 26 | 27 | # Download the Nginx source code 28 | nginx-source: 29 | file.managed: 30 | - name: /usr/local/src/nginx-1.4.1.tar.gz 31 | - source: http://nginx.org/download/nginx-1.4.1.tar.gz 32 | - source_hash: sha256=bca5d1e89751ba29406185e1736c390412603a7e6b604f5b4575281f6565d119 33 | 34 | # Run the Nginx install script 35 | nginx-install: 36 | cmd.script: 37 | - name: salt://nginx-passenger/install.sh 38 | - unless: /opt/nginx/sbin/nginx -v 2>&1 | grep 1.4.1 39 | - template: jinja 40 | - require: 41 | - file: nginx-source 42 | - cmd: passenger 43 | - file: /usr/local/bin/rake 44 | - file: /usr/local/bin/ruby 45 | 46 | # Generate the Nginx configuration file 47 | nginx-configuration: 48 | file.managed: 49 | - name: /opt/nginx/conf/nginx.conf 50 | - source: salt://nginx-passenger/nginx-conf 51 | - template: jinja 52 | - require: 53 | - cmd: nginx-install 54 | 55 | # Copy Nginx init script 56 | nginx-init-script: 57 | file.managed: 58 | - name: /etc/init.d/nginx 59 | - source: salt://nginx-passenger/nginx-init 60 | - mode: 755 61 | - require: 62 | - cmd: nginx-install 63 | 64 | # Enable the Nginx init script so the service will start at boot 65 | # Watch for changes to the configuration and init script, which will 66 | # trigger a restart 67 | nginx: 68 | service: 69 | - running 70 | - enable: True 71 | - watch: 72 | - file: nginx-configuration 73 | - file: nginx-init-script 74 | 75 | # Set up Nginx vhost directories 76 | {% for dir in 'sites-available', 'sites-enabled' %} 77 | /etc/nginx/{{ dir }}: 78 | file.directory: 79 | - makedirs: True 80 | - user: root 81 | - group: root 82 | - require: 83 | - cmd: nginx-install 84 | {% endfor %} 85 | 86 | # Set up log rotation for Nginx and Passenger 87 | {% for rotate_target in 'nginx', 'passenger' %} 88 | /etc/logrotate.d/{{ rotate_target }}: 89 | file.managed: 90 | - source: salt://nginx-passenger/{{ rotate_target }}-logrotate 91 | - require: 92 | - cmd: nginx-install 93 | - cmd: passenger 94 | {% endfor %} 95 | --------------------------------------------------------------------------------