Last active
August 7, 2019 21:54
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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