Öncelikle bu kadar uzun bir yazı yazdığım için özür dilerim, çünkü bu benim kendi kurallarıma aykırı birşey ama bu seferlik bunu olmamış varsayalım:)

Bu örneğimizde uygulamamızı DEV (development) sunucusunda geliştireceğiz, DEPLOY (deployment) sunucusundan STAG (staging) ve PROD (production) sunucularına capistrano yardımıyla ileteceğiz. Size bu işlemin nasıl yapılacağını göstereceğim ama CI (Continuous Integration) sunucusunu örneğin dışında tutacağım. Aksi taktirde örneğimiz gerçek çok uzun birşey olacak. Her ne kadar bunu söylesemde, Jenkins'i sisteminize nasıl entegre edeceğinizi gösteren yazılarımıda okuyabilirsiniz.


Bilmemiz gerekenler


Kurulum


Her ne kadar servisleri yüklemek için vagrant kullanacak olsakta, siz istediğinizi yapabilirsiniz. İsterseniz "bootstrap.sh" dosyalarına bakabilirsiniz.


Akış ve aşamaları


Mor bölüm "development" aşaması, yeşil bölüm ise "deployment" aşaması olarak geçer. Yapacağımız işlem kısacası budur ve de aslınca uygulama geliştirme bu şekilde olmalıdır. Daha önceden de belirttiğim gibi bu seferliğine Jenkins kısmını atlayacağız.



Sunucular



Development ve Jenkins sunucularının konfigürasyonları/kurulumu birbirlerine çok yakın olmalıdır, çünkü jenkins uygulamanızı, uygulamanız içindeki testleri çalıştırarak test edecektir. Staging ve Production sunucularının konfigürasyonları/kurulumu birbirlerine çok yakın olmalıdır, çünkü teknik olarak staging eşittir production.


Uygulamanın izole ortamları


Bazı şeyleri birbirlerinden ayrı tutmak için, uygulamamızı 4 farkli ortama ayıracağız. Ortamlardan dev development için, test testler için, stag staging için ve prod production için kullanılacak.


Güvenlik


Kullanıcıların yüklediği dosyalar, apache'nin varsayılan web klasöründe değilde özel bir yerde tutulacak. Buna kullanıcıların tüm şahsi dosyaları dahil. Halka açık dosyalar symlink ile ulaşıma açılacak, halka kapalı olan dosyalar ile programlama teknikleri ile ulaşıma açılacak.


İletim


Uygulamanızı sunuculara iletebilmek için, öncelikle "deployment" sunucusuna deployer kullanıcısını kullanarak SSH ile bağlanmanız gerekecektir. Bu kullanıcı sudo haklarına sahip olmayacak ama projenin tüm dosya ve klasörlerinin sahibi olacak. Bazı dosya ve klasörler, her iletim arasında paylaşılmış halde olacaktır. Bunun da tek nedeni, paylaşılan dosya ve klasörlerin her ne olursa olsun, bütün iletimler içinde kullanılabilir bir vaziyette olması gerekir. Örneğin cache, session, logs, kullanıcı dosyaları (resim, döküman) vs. gibi şeyler silinmemiş olur.


Uygulama


Config dosyaları


İzole ortamlar için toplam 4 tane config.yml dosyası olmalıdır. Sistemimizde config_dev.yml, config_test.yml ve config_prod.yml dosyaları zaten mevcut olacaktır. Siz config_prod.yml dosyasının bir kopyasını yaratarak config_stag.yml olarak adlandırın.


Parameter dosyaları


İzole ortamlar için toplam 3 tane parameters.yml.dist dosyası olmalıdır. Sistemimizde parameters.yml.dist dosyası zaten mevcut olacaktır. Siz parameters.yml.dist dosyasının iki kopyasını yaratıp parameters_staging.yml.dist ve parameters_production.yml.dist olarak adlandırın.


# parameters.yml.dist

parameters:
public_image_uploads_path: '%kernel.root_dir%/../web/%kernel.environment%/uploads/images'
private_image_uploads_path: '%kernel.root_dir%/Resources/private/uploads/images'
public_image_uploads_assets_path: '%kernel.environment%/uploads/images'

# parameters_staging.yml.dist

parameters:
public_image_uploads_path: '/srv/www-shared/football/uploads/public/images'
private_image_uploads_path: '/srv/www-shared/football/uploads/private/images'
public_image_uploads_assets_path: 'uploads/public/images'

# parameters_production.yml.dist

parameters:
public_image_uploads_path: '/srv/www-shared/football/uploads/public/images'
private_image_uploads_path: '/srv/www-shared/football/uploads/private/images'
public_image_uploads_assets_path: 'uploads/public/images'

Ön controller


İzole ortamlar için toplam 3 tane app.php dosyası olmalıdır. Sistemimizde app_dev.php dosyası zaten mevcut olacaktır. Siz app_dev.php dosyasının bir kopyasını yaratıp app_test.php olarak ve app.php dosyasının bir kopyasını yaratıp app_stag.php olarak adlandırın. İçerik farkları aşağıdaki gibi olacak.


# app_dev.php and app_test.php
...
$kernel = new AppKernel('dev', true); # For app_dev.php
$kernel = new AppKernel('test', true); # For app_test.php
...

# app.php and app_stag.php
...
$kernel = new AppKernel('prod', false); # For app.php
$kernel = new AppKernel('stag', false); # For app_stag.php
...

Dosya yükleme yolları


Uygulamanızda web/dev/uploads/images ve app/Resources/private/uploads/images klasörlerini yaratın.


.gitignore


...
/app/Resources/private/uploads/images/*
!app/Resources/private/uploads/images/.gitkeep
/web/dev/uploads/images/*
!web/dev/uploads/images/.gitkeep
...

.gitattributes


/deploy export-ignore
Capfile export-ignore
Gemfile export-ignore
Gemfile.lock export-ignore

Capistrano dosyaları


deploy.rb


Öncelikle Capistrano Flow ve Capistrano::Symfony sayfalarını giderek, before ve after kancaların kontrol edin.


# deploy/deploy.rb

# Locked capistrano version.
lock "3.8.0"

#---------------------
# APPLICATION
#---------------------
# The name of the application
set :application, "football"

# The path on the remote server where the application will be deployed
# Must have RW permissions by "deployer" user
set :deploy_to, "/srv/www/#{fetch(:application)}"

# The path on the remote server where the temporary files will be stored
# Must have RW permissions by "deployer" user
set :tmp_dir, "#{fetch(:deploy_to)}/tmp/capistrano"

# The application repository
set :repo_url, "git@github.com:Inanzzz/football.git"

# Asks branch to deploy
ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp

#---------------------
# SYMFONY
#---------------------
# Symfony command environment
set :symfony_env, fetch(:stage).to_s == "production" ? "prod" : "stag"

# Paths
set :vendor_path, "vendor"
set :web_uploads_public_path, "/web/uploads/public"
set :shared_uploads_path, "/srv/www-shared/football/uploads"
set :shared_uploads_public_images_path, "#{fetch(:shared_uploads_path)}/public/images"
set :shared_uploads_private_images_path, "#{fetch(:shared_uploads_path)}/private/images"

# START: Directory create
set :directories_to_create, [
fetch(:shared_uploads_public_images_path),
fetch(:shared_uploads_private_images_path)
]
after "deploy:check:directories", "application:directory:create"
# END: Directory create

# START: Shared folders between releases
set :linked_dirs, [
fetch(:var_path),
fetch(:vendor_path)
]
# END: Shared folders between releases

# START: File permissions
set :permission_method, :acl
set :file_permissions_users, ["www-data"]
set :file_permissions_paths, [fetch(:var_path)]
before "deploy:set_permissions:acl", "application:directory:permission"
# END: File permissions

# START: Override stage dependent files
set :files_to_override, [
[
"#{fetch(:app_config_path)}/parameters_#{fetch(:stage)}.yml.dist",
"#{fetch(:app_config_path)}/parameters.yml.dist"
]
]
before "composer:run", "application:file:override"
# END: Override stage dependent files

# START: Application necessary commands to run
set :application_commands_to_run, [
"assetic:dump --no-debug --env=#{fetch(:symfony_env)}",
"doctrine:schema:update --force --no-interaction --no-debug --env=#{fetch(:symfony_env)}"
]
after "composer:run", "application:command:run"
# START: Run application commands

# START: Application necessary symlinks
set :symlink_dirs, [
[
fetch(:shared_uploads_public_images_path),
fetch(:web_uploads_public_path)
]
]
after "deploy:symlink:release", "application:directory:symlink"
# END: Application necessary symlinks

# START: Artifacts to clear
set :artifacts_to_clear, [
"#{fetch(:app_config_path)}/config_test.yml",
"#{fetch(:app_config_path)}/config_dev.yml",
"#{fetch(:app_config_path)}/routing_dev.yml",
"#{fetch(:app_config_path)}/parameters.yml.dist",
"#{fetch(:web_path)}/config.php",
"#{fetch(:web_path)}/bundles",
"#{fetch(:web_path)}/dev"
]
after "deploy:cleanup", "application:artifact:clear"
# END: Artifacts to clear

application.rake


# deploy/tasks/application.rake

namespace :application do

namespace :directory do
desc "Creates non-existent directories"
task :create do
on roles(:app) do
puts "-" * 6
puts "Creates non-existent directories"
directories_to_create = *fetch(:directories_to_create)
directories_to_create.each do |directory|
if !test("[ -d #{directory} ]")
execute :mkdir, "-p", directory
end
end
puts "-" * 6
end
end

desc "Prepares +RW permissions for directories"
task :permission do
on roles(:app) do
puts "-" * 6
puts "Preparing +RW permissions for directories"
directories_to_create = *fetch(:directories_to_create)
directories_to_create.each do |directory|
if !test("[ -d #{directory} ]")
execute :mkdir, "-p", directory
else
if !test("[ \"$(ls -A #{directory})\" ]")
append :file_permissions_paths, directory
end
end
end
puts "-" * 6
end
end

desc "Symlink directories"
task :symlink do
on roles(:app) do
puts "-" * 6
puts "Symlinking directories"
symlink_dirs = *fetch(:symlink_dirs)
symlink_dirs.each do |directory|
source = directory[0]
destination = "#{release_path}#{directory[1]}"
if !test("[ -d #{source} ]")
execute :mkdir, "-p", source
end
if !test("[ -d #{destination} ]")
execute :mkdir, "-p", destination
end
execute :ln, "-s", "#{source} #{destination}"
end
puts "-" * 6
end
end
end

namespace :file do
desc "Overrides stage dependent files"
task :override do
on roles(:app) do
puts "-" * 6
puts "Overriding stage dependent files"
files_to_override = *fetch(:files_to_override)
files_to_override.each do |files|
source = "#{release_path}/#{files[0]}"
destination = "#{release_path}/#{files[1]}"
if test("[ -f #{source} ]")
execute :mv, "#{source} #{destination}"
else
Rake::Task["application:raise:exception"].invoke("Not found: #{source}")
end
end
puts "-" * 6
end
end
end

namespace :command do
desc "Runs application necessary commands"
task :run do
on roles (:app) do
puts "-" * 6
puts "Running application necessary commands"
commands = *fetch(:application_commands_to_run)
commands.each do |command|
symfony_console command
end
puts "-" * 6
end
end
end

namespace :artifact do
desc "Clears artifacts"
task :clear do
on roles(:app) do
puts "-" * 6
puts "Clearing artifacts"
artifacts_to_clear = *fetch(:artifacts_to_clear)
artifacts_to_clear.each do |artifact|
artifact = "#{release_path}/#{artifact}"
if test("[ -f #{artifact} ]")
execute :rm, artifact
elsif test("[ -d #{artifact} ]")
execute :rm, "-rf", artifact
else
Rake::Task["symfony:raise:exception"].invoke("Not found: #{artifact}")
end
end
puts "-" * 6
end
end
end

namespace :raise do
desc "Internal method to raise exception"
task :exception, [:message] do |task, args|
on roles(:app) do
raise "\033[0;31m" + args[:message] + "\033[0m"
end
end
end

end

staging.rb


# deploy/stages/staging.rb

# The settings for remote server.
server "192.168.99.40", user: "deployer", roles: %w{app db web}

#---------------------
# SYMFONY
#---------------------
# Artifacts to clear. Opposed to other stage
append :artifacts_to_clear,
"#{fetch(:app_config_path)}/parameters_production.yml.dist",
"#{fetch(:app_config_path)}/config_prod.yml"

#Override stage dependent files
append :files_to_override, [
"#{fetch(:web_path)}/app_stag.php",
"#{fetch(:web_path)}/app.php"
]

production.rb


# deploy/stages/production.rb

# The settings for remote server.
server "192.168.99.50", user: "deployer", roles: %w{app db web}

#---------------------
# SYMFONY
#---------------------
# Artifacts to clear. Opposed to other stage
append :artifacts_to_clear,
"#{fetch(:app_config_path)}/parameters_staging.yml.dist",
"#{fetch(:app_config_path)}/config_stag.yml"

Capfile


set :deploy_config_path, "deploy/deploy.rb"
set :stage_config_path, "deploy/stages"

require "capistrano/setup"
require "capistrano/deploy"
require "capistrano/symfony"
require "capistrano/scm/git"

install_plugin Capistrano::SCM::Git

Dir.glob('deploy/tasks/*.rake').each { |r| import r }

Gemfile


source 'https://rubygems.org'

gem 'capistrano', '~> 3.8'
gem 'capistrano-symfony', '~> 1.0.0.rc1'

Setup development environment (DEV - 192.168.99.10)


Vagrantfile


# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
# Box type
config.vm.box = "ubuntu/trusty64"
# Sync folder between host and guest machine
# Creates if doesn't exist and grants +RW permissions from host machine
config.vm.synced_folder "www", "/var/www/html", create: true, nfs: true, mount_options: ["actimeo=2"]

# Box settings
config.vm.define :vagrant do |vagrant_config|
# Terminal name
vagrant_config.vm.hostname = "dev"
# IP to access from host machine
vagrant_config.vm.network "private_network", ip: "192.168.99.10"
# Script to run while setting up the box
vagrant_config.vm.provision :shell, path: "bootstrap.sh"
end

# Oracle VM VirtualBox settings
config.vm.provider :virtualbox do |virtualbox_config|
# Box name
virtualbox_config.name = "Development"
# Alloved RAM
virtualbox_config.memory = 2048
# Allowed CPU core
virtualbox_config.cpus = 2
end
end

bootstrap.sh


#!/usr/bin/env bash

# BEGIN ########################################################################
echo -e "-- ------------------ --\n"
echo -e "-- BEGIN BOOTSTRAPING --\n"
echo -e "-- ------------------ --\n"

# VARIABLES ####################################################################
echo -e "-- GLOBAL VARIABLES\n"
echo -e "-- Setup variables\n"
APP_WEB_ROOT=/var/www/html/football/web
PHP_INI=/etc/php5/apache2/php.ini
APACHE_CONFIG=/etc/apache2/apache2.conf
PHPMYADMIN_CONFIG=/etc/phpmyadmin/config-db.php
SITES_ENABLED=/etc/apache2/sites-enabled
LOCALHOST=127.0.0.1
VHOST=football.${HOSTNAME}
VHOST_PORT=8081
MYSQL_DATABASE=football_${HOSTNAME}
MYSQL_USER=root
MYSQL_PASSWORD=root

# BOX ##########################################################################
echo -e "-- SYSTEM\n"
echo -e "-- Updating packages list\n"
apt-get update -y -qq

# APACHE #######################################################################
echo -e "-- APACHE\n"
echo -e "-- Installing Apache web server\n"
apt-get install -y apache2 > /dev/null 2>&1

echo -e "-- Adding ServerName to Apache config\n"
echo "ServerName ${LOCALHOST}" >> "${APACHE_CONFIG}"

echo -e "-- Allowing Apache override to all\n"
sed -i "s/AllowOverride None/AllowOverride All/g" ${APACHE_CONFIG}

echo -e "-- Creating vhost \n"
cat > ${SITES_ENABLED}/${VHOST}.conf <<EOF
<VirtualHost *:${VHOST_PORT}>
ServerName ${VHOST}
DocumentRoot ${APP_WEB_ROOT}

<Directory ${APP_WEB_ROOT}>
Options Indexes FollowSymlinks
AllowOverride All
Order allow,deny
Allow from all
Require all granted
</Directory>

ErrorLog ${APACHE_LOG_DIR}/${VHOST}-error.log
CustomLog ${APACHE_LOG_DIR}/${VHOST}-access.log combined
</VirtualHost>
EOF

echo -e "-- Enabling vhost \n"
a2ensite ${VHOST}.conf> /dev/null 2>&1

echo -e "-- Listening vhost port\n"
echo Listen ${VHOST_PORT} >> /etc/apache2/ports.conf

echo -e "-- Adding vhost to hosts\n"
echo ${LOCALHOST} ${VHOST} >> /etc/hosts

# PHP ##########################################################################
echo -e "-- PHP\n"
echo -e "-- Fetching PHP 5.6 repository\n"
add-apt-repository -y ppa:ondrej/php5-5.6 > /dev/null 2>&1

echo -e "-- Updating packages list\n"
apt-get update -y -qq

echo -e "-- Installing PHP modules\n"
apt-get install -y python-software-properties > /dev/null 2>&1
apt-get install -y libapache2-mod-php5 > /dev/null 2>&1
apt-get install -y libapache2-mod-fastcgi > /dev/null 2>&1
apt-get install -y php5 > /dev/null 2>&1
apt-get install -y php5-cli > /dev/null 2>&1
apt-get install -y php5-mcrypt > /dev/null 2>&1
apt-get install -y php5-curl > /dev/null 2>&1
apt-get install -y php5-dev > /dev/null 2>&1
apt-get install -y php5-fpm > /dev/null 2>&1

echo -e "-- Enabling PHP mcrypt module\n"
php5enmod mcrypt

echo -e "-- Turning PHP error reporting on\n"
sed -i "s/error_reporting = .*/error_reporting = E_ALL/" ${PHP_INI}
sed -i "s/display_errors = .*/display_errors = On/" ${PHP_INI}

# MYSQL ########################################################################
echo -e "-- MYSQL\n"
echo -e "-- Preparing MySQL server installation options\n"
debconf-set-selections <<< "mysql-server mysql-server/root_password password ${MYSQL_PASSWORD}"
debconf-set-selections <<< "mysql-server mysql-server/root_password_again password ${MYSQL_PASSWORD}"

echo -e "-- Installing MySQL server and relevant packages\n"
apt-get install -y mysql-server > /dev/null 2>&1
apt-get install -y libapache2-mod-auth-mysql > /dev/null 2>&1
apt-get install -y php5-mysql > /dev/null 2>&1

echo -e "-- Setting up application database\n"
mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -h ${LOCALHOST} -e "CREATE DATABASE IF NOT EXISTS ${MYSQL_DATABASE}"

# PHPMYADMIN ###################################################################
echo -e "-- PHPMYADMIN\n"
echo -e "-- Preparing phpMyAdmin installation options\n"
debconf-set-selections <<< "phpmyadmin phpmyadmin/dbconfig-install boolean true"
debconf-set-selections <<< "phpmyadmin phpmyadmin/app-password-confirm password ${MYSQL_PASSWORD}"
debconf-set-selections <<< "phpmyadmin phpmyadmin/mysql/admin-pass password ${MYSQL_PASSWORD}"
debconf-set-selections <<< "phpmyadmin phpmyadmin/mysql/app-pass password ${MYSQL_PASSWORD}"
debconf-set-selections <<< "phpmyadmin phpmyadmin/reconfigure-webserver multiselect apache2"

echo -e "-- Installing phpMyAdmin\n"
apt-get install -y phpmyadmin > /dev/null 2>&1

echo -e "-- Setting up phpMyAdmin GUI user\n"
sed -i "s/dbuser='phpmyadmin'/dbuser='${MYSQL_USER}'/g" ${PHPMYADMIN_CONFIG}

# SQLITE #######################################################################
echo -e "-- SQLITE\n"
echo -e "-- Installing SQLite and relative modules\n"
apt-get install -y php5-sqlite > /dev/null 2>&1
apt-get install -y sqlite3 > /dev/null 2>&1
apt-get install -y libsqlite3-dev > /dev/null 2>&1

# COMPOSER #####################################################################
echo -e "-- COMPOSER\n"
echo -e "-- Installing PHP cURL module\n"
apt-get install -y curl > /dev/null 2>&1

echo -e "-- Setting up Composer\n"
curl -sSk https://getcomposer.org/installer | php -- --disable-tls > /dev/null 2>&1
mv composer.phar /usr/local/bin/composer

# GIT ##########################################################################
echo -e "-- GIT\n"
echo -e "-- Installing Git\n"
apt-get install -y git > /dev/null 2>&1

# GITHUB #######################################################################
echo -e "-- GITHUB\n"
echo -e "-- Enabling SSH connection to GitHub\n"
mkdir -p ~/.ssh > /dev/null 2>&1
ssh-keyscan -H github.com >> ~/.ssh/known_hosts > /dev/null 2>&1

# REFRESH ######################################################################
echo -e "-- RESTART\n"
echo -e "-- Restarting Apache web server\n"
service apache2 restart

# END ##########################################################################
echo -e "-- ---------------- --"
echo -e "-- END BOOTSTRAPING --"
echo -e "-- ---------------- --"

GitHub SSH entegrasyonu


$ ssh-keygen -t rsa -b 4096 -C "inanzzz@domain.com"
$ eval "$(ssh-agent -s)"
$ ssh-add ~/.ssh/id_rsa
$ cat ~/.ssh/id_rsa.pub #Copy this key and add it to GitHub
$ ssh -T git@github.com

Git repository kopyalama


$ git clone git@github.com:Inanzzz/football.git /var/www/html/football

Git konfigürasyonu


$ git config --global user.name "Inanzzz"
$ git config --global user.email "inanzzz@domain.com"

Setup staging environment (STAG - 192.168.99.40)


Vagrantfile


# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
# Box type
config.vm.box = "ubuntu/trusty64"

# Box settings
config.vm.define :vagrant do |vagrant_config|
# Terminal name
vagrant_config.vm.hostname = "stag"
# IP to access from host machine
vagrant_config.vm.network "private_network", ip: "192.168.99.40"
# Script to run while setting up the box
vagrant_config.vm.provision :shell, path: "bootstrap.sh"
end

# Oracle VM VirtualBox settings
config.vm.provider :virtualbox do |virtualbox_config|
# Box name
virtualbox_config.name = "Staging"
# Sync host and guest machine clocks
virtualbox_config.customize [
"guestproperty",
"set",
:id,
"/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold",
1000
]
end
end

bootstrap.sh


#!/usr/bin/env bash

# BEGIN ########################################################################
echo -e "-- ------------------ --\n"
echo -e "-- BEGIN BOOTSTRAPING --\n"
echo -e "-- ------------------ --\n"

# VARIABLES ####################################################################
echo -e "-- GLOBAL VARIABLES\n"
echo -e "-- Setup variables\n"
WEB_ROOT=/srv/www
APP_WEB_ROOT=/football/current/web
PHP_INI=/etc/php5/apache2/php.ini
APACHE_CONFIG=/etc/apache2/apache2.conf
LOCALHOST=127.0.0.1
APP_NAME=football
MYSQL_DATABASE=football_${HOSTNAME}
MYSQL_USER=root
MYSQL_PASSWORD=root

# BOX ##########################################################################
echo -e "-- SYSTEM\n"
echo -e "-- Updating packages list\n"
apt-get update -y -qq

# APACHE #######################################################################
echo -e "-- APACHE\n"
echo -e "-- Installing Apache web server\n"
apt-get install -y apache2 > /dev/null 2>&1

echo -e "-- Adding ServerName to Apache config\n"
echo "ServerName ${LOCALHOST}" >> "${APACHE_CONFIG}"

echo -e "-- Creating vhost \n"
cat > /etc/apache2/sites-available/000-default.conf <<EOF
<VirtualHost *:80>
DocumentRoot ${WEB_ROOT}${APP_WEB_ROOT}

<Directory ${WEB_ROOT}${APP_WEB_ROOT}>
AllowOverride All
Require all granted
Allow from All

<IfModule mod_rewrite.c>
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ app.php [QSA,L]
</IfModule>
</Directory>

ErrorLog ${APACHE_LOG_DIR}/${APP_NAME}-error.log
CustomLog ${APACHE_LOG_DIR}/${APP_NAME}-access.log combined
</VirtualHost>
EOF

# PHP ##########################################################################
echo -e "-- PHP\n"
echo -e "-- Fetching PHP 5.6 repository\n"
add-apt-repository -y ppa:ondrej/php5-5.6 > /dev/null 2>&1

echo -e "-- Updating packages list\n"
apt-get update -y -qq

echo -e "-- Installing PHP modules\n"
apt-get install -y python-software-properties > /dev/null 2>&1
apt-get install -y libapache2-mod-php5 > /dev/null 2>&1
apt-get install -y libapache2-mod-fastcgi > /dev/null 2>&1
apt-get install -y php5 > /dev/null 2>&1
apt-get install -y php5-cli > /dev/null 2>&1
apt-get install -y php5-mcrypt > /dev/null 2>&1
apt-get install -y php5-curl > /dev/null 2>&1
apt-get install -y php5-dev > /dev/null 2>&1
apt-get install -y php5-fpm > /dev/null 2>&1

echo -e "-- Enabling PHP mcrypt module\n"
php5enmod mcrypt

echo -e "-- Preventing web directory listing\n"
a2dismod autoindex

echo -e "-- Enabling mod_rewrite module\n"
a2enmod rewrite

# MYSQL ########################################################################
echo -e "-- MYSQL\n"
echo -e "-- Preparing MySQL server installation options\n"
debconf-set-selections <<< "mysql-server mysql-server/root_password password ${MYSQL_PASSWORD}"
debconf-set-selections <<< "mysql-server mysql-server/root_password_again password ${MYSQL_PASSWORD}"

echo -e "-- Installing MySQL server and relevant packages\n"
apt-get install -y mysql-server > /dev/null 2>&1
apt-get install -y libapache2-mod-auth-mysql > /dev/null 2>&1
apt-get install -y php5-mysql > /dev/null 2>&1

echo -e "-- Setting up application database\n"
mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -h ${LOCALHOST} -e "CREATE DATABASE IF NOT EXISTS ${MYSQL_DATABASE}"

# GIT ##########################################################################
echo -e "-- GIT\n"
echo -e "-- Installing Git\n"
apt-get install -y git > /dev/null 2>&1

# COMPOSER #####################################################################
echo -e "-- COMPOSER\n"
echo -e "-- Installing PHP cURL module\n"
apt-get install -y curl > /dev/null 2>&1

echo -e "-- Setting up Composer\n"
curl -sSk https://getcomposer.org/installer | php -- --disable-tls > /dev/null 2>&1
mv composer.phar /usr/local/bin/composer

# REFRESH ######################################################################
echo -e "-- RESTART\n"
echo -e "-- Restarting Apache web server\n"
service apache2 restart

# END ##########################################################################
echo -e "-- ---------------- --"
echo -e "-- END BOOTSTRAPING --"
echo -e "-- ---------------- --"

Apache konfigürasyonu


vagrant@stag:~$ sudo nano /etc/apache2/apache2.conf

# Replace this
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>

# With this
<Directory /srv/www/>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>

vagrant@stag:~$ sudo service apache2 restart

Deployment kullanıcısı yaratma


vagrant@stag:~$ sudo su -l root
root@stag:~# adduser deployer

Proje klasörü yaratma


root@stag:~# mkdir /srv/www
root@stag:~# mkdir /srv/www-shared
root@stag:~# chown -R deployer /srv/www
root@stag:~# chown -R deployer /srv/www-shared

GitHub SSH entegrasyonu


root@stag:~# su -l deployer
deployer@stag:~$ ssh-keygen -t rsa -b 4096 -C "inanzzz@domain.com"
deployer@stag:~$ eval "$(ssh-agent -s)"
deployer@stag:~$ ssh-add ~/.ssh/id_rsa
deployer@stag:~$ cat ~/.ssh/id_rsa.pub # Copy this key and add it to GitHub
deployer@stag:~$ ssh -T git@github.com

Setup production environment (PROD - 192.168.99.50)


Vagrantfile


# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
# Box type
config.vm.box = "ubuntu/trusty64"

# Box settings
config.vm.define :vagrant do |vagrant_config|
# Terminal name
vagrant_config.vm.hostname = "prod"
# IP to access from host machine
vagrant_config.vm.network "private_network", ip: "192.168.99.50"
# Script to run while setting up the box
vagrant_config.vm.provision :shell, path: "bootstrap.sh"
end

# Oracle VM VirtualBox settings
config.vm.provider :virtualbox do |virtualbox_config|
# Box name
virtualbox_config.name = "Production"
# Sync host and guest machine clocks
virtualbox_config.customize [
"guestproperty",
"set",
:id,
"/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold",
1000
]
end
end

bootstrap.sh


#!/usr/bin/env bash

# BEGIN ########################################################################
echo -e "-- ------------------ --\n"
echo -e "-- BEGIN BOOTSTRAPING --\n"
echo -e "-- ------------------ --\n"

# VARIABLES ####################################################################
echo -e "-- GLOBAL VARIABLES\n"
echo -e "-- Setup variables\n"
WEB_ROOT=/srv/www
APP_WEB_ROOT=/football/current/web
PHP_INI=/etc/php5/apache2/php.ini
APACHE_CONFIG=/etc/apache2/apache2.conf
LOCALHOST=127.0.0.1
APP_NAME=football
MYSQL_DATABASE=football_${HOSTNAME}
MYSQL_USER=root
MYSQL_PASSWORD=root

# BOX ##########################################################################
echo -e "-- SYSTEM\n"
echo -e "-- Updating packages list\n"
apt-get update -y -qq

# APACHE #######################################################################
echo -e "-- APACHE\n"
echo -e "-- Installing Apache web server\n"
apt-get install -y apache2 > /dev/null 2>&1

echo -e "-- Adding ServerName to Apache config\n"
echo "ServerName ${LOCALHOST}" >> "${APACHE_CONFIG}"

echo -e "-- Creating vhost \n"
cat > /etc/apache2/sites-available/000-default.conf <<EOF
<VirtualHost *:80>
DocumentRoot ${WEB_ROOT}${APP_WEB_ROOT}

<Directory ${WEB_ROOT}${APP_WEB_ROOT}>
AllowOverride All
Require all granted
Allow from All

<IfModule mod_rewrite.c>
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ app.php [QSA,L]
</IfModule>
</Directory>

ErrorLog ${APACHE_LOG_DIR}/${APP_NAME}-error.log
CustomLog ${APACHE_LOG_DIR}/${APP_NAME}-access.log combined
</VirtualHost>
EOF

# PHP ##########################################################################
echo -e "-- PHP\n"
echo -e "-- Fetching PHP 5.6 repository\n"
add-apt-repository -y ppa:ondrej/php5-5.6 > /dev/null 2>&1

echo -e "-- Updating packages list\n"
apt-get update -y -qq

echo -e "-- Installing PHP modules\n"
apt-get install -y python-software-properties > /dev/null 2>&1
apt-get install -y libapache2-mod-php5 > /dev/null 2>&1
apt-get install -y libapache2-mod-fastcgi > /dev/null 2>&1
apt-get install -y php5 > /dev/null 2>&1
apt-get install -y php5-cli > /dev/null 2>&1
apt-get install -y php5-mcrypt > /dev/null 2>&1
apt-get install -y php5-curl > /dev/null 2>&1
apt-get install -y php5-dev > /dev/null 2>&1
apt-get install -y php5-fpm > /dev/null 2>&1

echo -e "-- Enabling PHP mcrypt module\n"
php5enmod mcrypt

echo -e "-- Preventing web directory listing\n"
a2dismod autoindex

echo -e "-- Enabling mod_rewrite module\n"
a2enmod rewrite

# MYSQL ########################################################################
echo -e "-- MYSQL\n"
echo -e "-- Preparing MySQL server installation options\n"
debconf-set-selections <<< "mysql-server mysql-server/root_password password ${MYSQL_PASSWORD}"
debconf-set-selections <<< "mysql-server mysql-server/root_password_again password ${MYSQL_PASSWORD}"

echo -e "-- Installing MySQL server and relevant packages\n"
apt-get install -y mysql-server > /dev/null 2>&1
apt-get install -y libapache2-mod-auth-mysql > /dev/null 2>&1
apt-get install -y php5-mysql > /dev/null 2>&1

echo -e "-- Setting up application database\n"
mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -h ${LOCALHOST} -e "CREATE DATABASE IF NOT EXISTS ${MYSQL_DATABASE}"

# GIT ##########################################################################
echo -e "-- GIT\n"
echo -e "-- Installing Git\n"
apt-get install -y git > /dev/null 2>&1

# COMPOSER #####################################################################
echo -e "-- COMPOSER\n"
echo -e "-- Installing PHP cURL module\n"
apt-get install -y curl > /dev/null 2>&1

echo -e "-- Setting up Composer\n"
curl -sSk https://getcomposer.org/installer | php -- --disable-tls > /dev/null 2>&1
mv composer.phar /usr/local/bin/composer

# REFRESH ######################################################################
echo -e "-- RESTART\n"
echo -e "-- Restarting Apache web server\n"
service apache2 restart

# END ##########################################################################
echo -e "-- ---------------- --"
echo -e "-- END BOOTSTRAPING --"
echo -e "-- ---------------- --"

Apache configuration


vagrant@prod:~$ sudo nano /etc/apache2/apache2.conf

# Replace this
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>

# With this
<Directory /srv/www/>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>

vagrant@prod:~$ sudo service apache2 restart

Deployment kullanıcısı yaratma


vagrant@prod:~$ sudo su -l root
root@prod:~# adduser deployer

Proje klasörü yaratma


root@prod:~# mkdir /srv/www
root@prod:~# mkdir /srv/www-shared
root@prod:~# chown -R deployer /srv/www
root@prod:~# chown -R deployer /srv/www-shared

GitHub SSH entegrasyonu


root@stag:~# su -l deployer
deployer@prod:~$ ssh-keygen -t rsa -b 4096 -C "inanzzz@domain.com"
deployer@prod:~$ eval "$(ssh-agent -s)"
deployer@prod:~$ ssh-add ~/.ssh/id_rsa
deployer@prod:~$ cat ~/.ssh/id_rsa.pub # Copy this key and add it to GitHub
deployer@prod:~$ ssh -T git@github.com

Setup deployment environment (DEPLOY - 192.168.99.30)


Vagrantfile


# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
# Box type
config.vm.box = "ubuntu/trusty64"

# Box settings
config.vm.define :vagrant do |vagrant_config|
# Terminal name
vagrant_config.vm.hostname = "deploy"
# IP to access from host machine
vagrant_config.vm.network "private_network", ip: "192.168.99.30"
# Script to run while setting up the box
vagrant_config.vm.provision :shell, path: "bootstrap.sh"
end

# Oracle VM VirtualBox settings
config.vm.provider :virtualbox do |virtualbox_config|
# Box name
virtualbox_config.name = "Deployment"
end
end

bootstrap.sh


#!/usr/bin/env bash

# BEGIN ########################################################################
echo -e "-- ------------------ --\n"
echo -e "-- BEGIN BOOTSTRAPING --\n"
echo -e "-- ------------------ --\n"

# BOX ##########################################################################
echo -e "-- SYSTEM\n"
echo -e "-- Updating packages list\n"
apt-get update -y -qq

# RUBY #########################################################################
echo -e "-- RUBY\n"
echo -e "-- Fetching Ruby repository\n"
add-apt-repository -y ppa:brightbox/ruby-ng > /dev/null 2>&1

echo -e "-- Updating packages list\n"
apt-get update -y -qq

echo -e "-- Installing Ruby\n"
apt-get install -y ruby2.2 > /dev/null 2>&1

# BUNDLER ######################################################################
echo -e "-- BUNDLER\n"
echo -e "-- Installing Ruby gems\n"
gem install bundler > /dev/null 2>&1

# CAPISTRANO ###################################################################
echo -e "-- CAPISTRANO\n"
echo -e "-- Installing Capistrano\n"
gem install capistrano > /dev/null 2>&1
gem install capistrano-ext > /dev/null 2>&1

# GIT ##########################################################################
echo -e "-- GIT\n"
echo -e "-- Installing Git\n"
apt-get install -y git > /dev/null 2>&1

# END ##########################################################################
echo -e "-- ---------------- --"
echo -e "-- END BOOTSTRAPING --"
echo -e "-- ---------------- --"

Deployment kullanıcısı yaratma


vagrant@deploy:~$ sudo su -l root
root@deploy:~# adduser deployer

Proje klasörü yaratma


root@deploy:~# mkdir /projects
root@deploy:~# chown -R deployer /projects

GitHub SSH entegrasyonu


root@deploy:~# su -l deployer
deployer@deploy:~$ ssh-keygen -t rsa -b 4096 -C "inanzzz@domain.com"
deployer@deploy:~$ eval "$(ssh-agent -s)"
deployer@deploy:~$ ssh-add ~/.ssh/id_rsa
deployer@deploy:~$ cat ~/.ssh/id_rsa.pub # Copy this key and add it to GitHub
deployer@deploy:~$ ssh -T git@github.com

Kullanıcıyı yetkilendirme


deployer@deploy:~$ cat ~/.ssh/id_rsa.pub | ssh deployer@192.168.99.40 'cat >> /home/deployer/.ssh/authorized_keys'
deployer@deploy:~$ cat ~/.ssh/id_rsa.pub | ssh deployer@192.168.99.50 'cat >> /home/deployer/.ssh/authorized_keys'

Git repository kopyalama


deployer@deploy:~$ git clone git@github.com:Inanzzz/football.git /projects/football

Bundler kurulumu


deployer@deploy:/projects/football$ bundle install

Testler


deployer@deploy:/projects/football$ bundle exec cap staging deploy
deployer@deploy:/projects/football$ bundle exec cap production deploy

İletimlerden sonra oluşak dosya ve klasör yapıları.


deployer@prod:~$ ls -l /srv/
drwxr-xr-x 3 deployer root 4096 Apr 26 09:48 www
drwxr-xr-x 3 deployer root 4096 Apr 26 09:49 www-shared

deployer@prod:~$ ls -l /srv/www-shared/football/uploads/public/images/
-rw-r--r-- 1 www-data www-data 627 Apr 26 09:56 open_lock.png
-rw-r--r-- 1 www-data www-data 1167 Apr 26 09:56 open_sign.png

deployer@prod:~$ ls -l /srv/www-shared/football/uploads/private/images/
-rw-r--r-- 1 www-data www-data 627 Apr 26 09:56 closed_lock.png
-rw-r--r-- 1 www-data www-data 1167 Apr 26 09:56 closed_sign.png

deployer@prod:~$ ls -l /srv/www/football/
lrwxrwxrwx 1 deployer deployer 41 Apr 26 09:56 current -> /srv/www/football/releases/20170417235339
drwxrwxr-x 6 deployer deployer 4096 Apr 26 09:56 releases
drwxrwxr-x 7 deployer deployer 4096 Apr 26 09:56 repo
-rw-rw-r-- 1 deployer deployer 216 Apr 26 09:56 revisions.log
drwxrwxr-x 4 deployer deployer 4096 Apr 26 09:49 shared
drwxrwxr-x 3 deployer deployer 4096 Apr 26 09:48 tmp

deployer@prod:~$ ls -l /srv/www/football/shared/
drwxrwxr-x+ 5 deployer deployer 4096 Apr 26 09:56 var
drwxrwxr-x 16 deployer deployer 4096 Apr 26 09:56 vendor

deployer@prod:~$ ls -l /srv/www/football/releases/20170417235339/
drwxrwxr-x 4 deployer deployer 4096 Apr 17 19:35 app
drwxrwxr-x 2 deployer deployer 4096 Apr 26 09:56 bin
-rw-rw-r-- 1 deployer deployer 2518 Apr 17 19:35 composer.json
-rw-rw-r-- 1 deployer deployer 85632 Apr 17 19:35 composer.lock
-rw-rw-r-- 1 deployer deployer 1065 Apr 17 19:35 LICENSE
-rw-rw-r-- 1 deployer deployer 978 Apr 17 19:35 phpunit.xml.dist
-rw-rw-r-- 1 deployer deployer 66 Apr 17 19:35 README.md
-rw-rw-r-- 1 deployer deployer 41 Apr 26 09:56 REVISION
drwxrwxr-x 3 deployer deployer 4096 Apr 17 19:35 src
lrwxrwxrwx 1 deployer deployer 28 Apr 26 09:56 var -> /srv/www/football/shared/var
lrwxrwxrwx 1 deployer deployer 31 Apr 26 09:56 vendor -> /srv/www/football/shared/vendor
drwxrwxr-x 6 deployer deployer 4096 Apr 26 09:56 web

deployer@prod:~$ ls -l /srv/www/football/releases/20170417235339/web/uploads/public/
lrwxrwxrwx 1 deployer deployer 46 Apr 26 09:56 images -> /srv/www-shared/football/uploads/public/images

deployer@prod:~$ ls -l /srv/www/football/releases/20170417235339/web/uploads/public/images/
-rw-r--r-- 1 www-data www-data 627 Apr 26 09:56 open_lock.png
-rw-r--r-- 1 www-data www-data 1167 Apr 26 09:56 open_sign.png