Skip to content

Instantly share code, notes, and snippets.

@alflanagan
Last active August 7, 2019 21:54
Show Gist options
  • Save alflanagan/7f26b5d136184bba23360d19202151c6 to your computer and use it in GitHub Desktop.
Save alflanagan/7f26b5d136184bba23360d19202151c6 to your computer and use it in GitHub Desktop.
A script to set up an AWS CentOS instance to run a web app.
#!/usr/bin/env bash
# This script creates a deployment user and sets up the entire environment for a Ruby on Rails app
# deploy to an AWS CentOS server.
# Tested using AMI:
# "CentOS Linux 7 x86_64 HVM EBS ENA 1805_01-b7ee8a69-ee97-4a49-9e68-afaee216db2e-ami-77ec9308.4 (ami-9887c6e7)"
# On a fresh AWS server:
# 1. sudo yum install git
# 2. sftp config/master.key into centos home dir.
# 3. git clone https://gist.github.com/7f26b5d136184bba23360d19202151c6.git
# 4. cd 7f26b5d136184bba23360d19202151c6
# 5. sudo ./aws_ruby_rails_setup.sh
# 6. sftp config/master.key to the config directory
# 7. sudo postgres and change database password to match value in credentials.
# 8. sudo vi /var/lib/pgsql/data/pg_hba.conf
# 10. sudo systemctl restart postgresql
# 11. fix the damn selinux setup
#### Constants
RVERSION=2.6.3
NODE_VERSION='lts/*'
ADMIN_USER=deploy
PSQL_CONF=/var/lib/pgsql/data/pg_hba.conf
RVM_SCRIPT=/usr/local/rvm/scripts/rvm
APP_ROOT=/var/www/devcampportfolio
export ADMIN_USER NODE_VERSION RVERSION PSQL_CONF
#### Commands
# don't show progress, do show errors
CURL="command curl -s -S"
GPG=gpg2
BUNDLE="bin/bundle"
# someday CentOS will catch up
DNF=yum
export CURL GPG BUNDLE DNF
#### required RPMs
RUBY_BUILD_DEPS="patch autoconf automake bison gcc-c++ libffi-devel libtool patch readline-devel"
RUBY_BUILD_DEPS="${RUBY_BUILD_DEPS} ruby sqlite-devel zlib-devel glibc-headers glibc-devel "
RUBY_BUILD_DEPS="${RUBY_BUILD_DEPS} openssl-devel bzip2-devel bzip2"
POSTGRES_DEPS="postgresql postgresql-libs postgresql-contrib postgresql-devel postgresql-server"
OTHER_DEPS="curl git"
export RUBY_BUILD_DEPS POSTGRES_DEPS OTHER_DEPS
#### useful functions
exit_with_error () {
echo "$1" >&2
exit "$2"
}
export -f exit_with_error
create_app_user () {
echo "${FUNCNAME[0]} entered"
grep -q $1 /etc/passwd && echo "${FUNCNAME[0]} exited, user exists." && return
useradd $1 -c"DevcampPortfolio App" -m -U
mkdir -p /home/$1/.ssh
touch /home/$1/.ssh/authorized_keys
sh -c "cat /home/centos/.ssh/authorized_keys >> /home/$1/.ssh/authorized_keys"
chown -R $1: /home/$1/.ssh
chmod 700 /home/$1/.ssh
sh -c "chmod 600 /home/$1/.ssh/*"
cat << EOF >> /home/$1/.bashrc
. ${RVM_SCRIPT}
rvm use 2.6.3
EOF
}
fix_selinux_perms () {
semanage fcontext -a -t httpd_sys_content_t "/var/www(/.*)?"
restorecon -R /var/www/devcampportfolio
}
# need to install in user directory
install_node () {
echo "${FUNCNAME[0]} entered"
# don't use NVM_DIR for this variable, it's used by nvm
local NVM_HOME=/home/$1/.nvm
if [[ -d ${NVM_HOME} ]]; then
echo "${FUNCNAME[0]}: exiting, already done!"
return
else
echo "${FUNCNAME[0]}: Downloading nvm"
sudo -u $1 -H bash -lc "cd; ${CURL} -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash"
fi
sudo -u $1 -H bash -lc "cd; . ${NVM_HOME}/nvm.sh; nvm install ${NODE_VERSION}; nvm alias default ${NODE_VERSION}"
local ERR=$?
echo "${FUNCNAME[0]} completed."
return $ERR
}
install_nginx_passenger () {
yum install -y epel-release || exit_with_error "Failure installing EPEL repository" 1
yum-config-manager --enable epel
yum clean all && yum update -y
# install passenger version of nginx
yum install -y pygpgme curl
local PASS_REPO=/etc/yum.repos.d/passenger.repo
local PASS_REPO_URL=https://oss-binaries.phusionpassenger.com/yum/definitions/el-passenger.repo
curl --fail -sSLo ${PASS_REPO} ${PASS_REPO_URL} || exit_with_error "Failure retrieving passenger repository" 2
yum install -y nginx passenger || yum-config-manager --enable cr && yum install -y nginx passenger
rpm -q passenger || exit_with_error "Failed to install passenger!" 3
}
setup_passenger () {
local PASS_CONF=/etc/nginx/conf.d/passenger.conf
local PASS_RUBY=/usr/local/rvm/rubies/ruby-2.6.3/bin/ruby
grep "${PASS_RUBY}" "${PASS_CONF}" && echo "edit_passenger_conf already ran" && return
cat <<- EOF >> ${PASS_CONF}
passenger_root /usr/share/ruby/vendor_ruby/phusion_passenger/locations.ini;
passenger_ruby ${PASS_RUBY};
passenger_instance_registry_dir /var/run/passenger-instreg;
EOF
systemctl restart nginx
systemctl enable nginx
/usr/bin/passenger-config validate-install --auto
/usr/sbin/passenger-memory-stats 2> /dev/null | grep '[P]assenger core' > /dev/null || exit_with-error "Passenger process is not running!" 9
echo "Completed Passenger setup"
}
# we have to have rvm installed for the admin user, as we'll need rvmsudo
install_ruby () {
echo "provision: ${FUNCNAME[0]} entered as ${USER}"
if [[ -f "${RVM_SCRIPT}" ]]; then
echo "${FUNCNAME[0]} exited, rvm and ruby already installed"
return
fi
# shellcheck disable=SC2164
cd
${CURL} -sSL https://rvm.io/mpapis.asc | ${GPG} --import -
${CURL} -sSL https://rvm.io/pkuczynski.asc | ${GPG} --import -
${CURL} -sSL https://get.rvm.io | bash -s stable --rails || exit_with_error 'Unable to install rvm or ruby' 4
# don't bother checking foreign script
# shellcheck disable=SC1091
. ${RVM_SCRIPT}
rvm install "${RVERSION}" || exit_with_error "Unable to install ruby version ${RVERSION}" 5
runuser root bash -c "echo 'export rvm_secure_path=1' >> /etc/profile.d/rvm_secure_path.sh"
gem install bundler --no-document
echo "${FUNCNAME[0]} completed."
}
clone_repo () {
local SOURCE=https://github.com/alflanagan/DevcampPortfolio.git
local BRANCH=master
echo "${FUNCNAME[0]} entered"
[[ -z "$1" ]] && exit_with_error "${FUNCNAME[0]} requires param, user name" 9
[[ -d ${APP_ROOT} ]] && echo "${FUNCNAME[0]} exiting, ${APP_ROOT} exists!" && return
mkdir -p ${APP_ROOT}
sudo chown $1: ${APP_ROOT}
cd ${APP_ROOT} || exit_with_error "Failed to create directory ${APP_ROOT}" 10
sudo -u $1 -H git clone ${SOURCE} code
sudo -u $1 -H bash -c "cd code; git checkout ${BRANCH}"
mv /home/centos/master.key code/config
chown $1:$1 code/config/master.key
chmod 400 code/config/master.key
}
setup_ruby_gems () {
cd ${APP_ROOT}/code || exit_with_error "Failed to cd to code directory" 11
# we need bash -l here to set rvm up
sudo -u $1 -H bash -lc "bundle install --deployment --without development test -j 2 --path=vendor/bundle"
}
initialize_app () {
sudo -u $1 -H bash -lc 'cd; bundle exec rake assets:precompile db:migrate RAILS_ENV=production'
}
setup_postgres () {
local TEMP_SCRIPT=/tmp/temp_user.sql
echo "provision: ${FUNCNAME[0]} entered as ${USER}"
#### get RPMS
# shellcheck disable=SC2086
${DNF} install -y ${POSTGRES_DEPS} || exit_with_error "${DNF} install of PostgresSQL failed!" 6
#### init data directories
runuser postgres -c "postgresql-setup initdb"
#### start it
systemctl enable postgresql
systemctl start postgresql
# enable password access for devcampportfolio user
# NOTE: This doesn't work as given. It must come before local peer login
# setting, or that setting must be disabled
echo "local devcampportfolio_production devcampportfolio md5" >> ${PSQL_CONF}
### Set up database user. You MUST change this pasword!
cat << EOF >> ${TEMP_SCRIPT}
CREATE USER devcampportfolio CREATEDB PASSWORD 'dumbpassword';
CREATE DATABASE devcampportfolio_production;
GRANT ALL ON DATABASE devcampportfolio_production TO devcampportfolio;
EOF
runuser postgres -c "psql -f ${TEMP_SCRIPT}" && rm ${TEMP_SCRIPT}
local ERR=$?
echo "provision: ${FUNCNAME[0]} completed."
return $ERR
}
create_nginx_config () {
cat << EOF >> /etc/nginx/conf.d/devcampportfolio.conf
server {
listen 80;
server_name alloydflanagan.com;
# Tell Nginx and Passenger where your app's 'public' directory is
root /var/www/devcampportfolio/code/public;
# Turn on Passenger
passenger_enabled on;
passenger_ruby /usr/local/rvm/gems/ruby-2.6.3/wrappers/ruby;
}
EOF
systemctl restart nginx
}
#### get RPMS
# shellcheck disable=SC2086
${DNF} install -y ${RUBY_BUILD_DEPS} || exit_with_error "${DNF} install of package failed!" 7
# why does shellcheck flag this and not other vars?
# shellcheck disable=SC2086
${DNF} install -y ${OTHER_DEPS} || exit_with_error "Can't install a dependency!" 8
rpm -q postgresql-server > /dev/null || setup_postgres
install_ruby
install_nginx_passenger
setup_passenger
create_app_user devcampportfolio
install_node devcampportfolio
clone_repo devcampportfolio
setup_ruby_gems devcampportfolio
initialize_app devcampportfolio
create_nginx_config
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment