Laravel Nginx Konfigürasyonu, Git ve Capistrano ile Kolay Deployment

Bu yazıda, yeni kurulmuş bir Ubuntu sunucu üzerinde, Laravel için Nginx'i nasıl yapılandırmanız gerektiğini, aynı zamanda ileride hiç başınızı ağrıtmayacak şekilde sunucunuzu git bazlı deployment'a nasıl hazır getirebileceğinizi anlatacağım. Örnek deployment aracı olarak Capistrano ile anlatacağım ama dilerseniz başka bir araç ile de aynı işlemleri yapabilirsiniz, aralarında çok ufak farklar oluyor.

Not: Bu yazıda, deployer hesabınızın hazır ve SSH ile public key doğrulamasıyla sunucuya ile bağlanabildiğini varsayıyorum. Eğer nasıl yapılacağını bilmiyorsanız, Linux Sunucularda Temel Güvenlik Önlemleri yazımı okumanızı öneririm.

Sunucunuza bağlandıktan sonra, sırayla
$ su - # bunu kolaylık olsun diye yazıyorum, root şifrenizi yazmanız lazım, ya da alttaki komutların başına sudo ekleyin
$ add-apt-repository ppa:ondrej/php
$ apt-get update
$ apt-get upgrade
$ apt-get install acl nginx mcrypt keychain memcached anacron redis-server php7.1-fpm php7.1-cli php7.1-mcrypt php7.1-cgi php7.1-zip php7.1-mbstring php7.1-curl php7.1-gd php7.1-intl php7.1-mysql php7.1-bz2 php-memcached php-xml git mysql-server mysql-client # paketlerden nginx, php7.1-fpm, php7.1-mcrypt, git, keychain dışındakiler opsiyonel
$ curl -sS https://getcomposer.org/installer | php
$ mv composer.phar /usr/local/bin/composer
Kurulumları tamamladıktan sonra, barındıracağınız sisteminiz için gerekli klasörleri oluşturmanız lazım.
$ mkdir /var/www/domain.com -p
$ mkdir /var/log/nginx/domain.com
Bu aşamadan sonra nginx için sunucu ayarını yapabiliriz.
$ cp /etc/nginx/sites-available/default /etc/nginx/sites-available/domain.com
$ ln -s /etc/nginx/sites-available/domain.com /etc/nginx/sites-enabled/domain.com
$ rm /etc/nginx/sites-enabled/default
$ nano /etc/nginx/sites-available/domain.com
Tüm dosyayı CTRL + K'ya basılı tutarak temizleyip, alttaki konfigürasyonu kullanabilirsiniz.
# www'lu domaine gelen isteği www'suz domaine yönlendirmek için

server {
    listen          80;
    server_name     www.domain.com;
    rewrite ^(.*)   http://domain.com$1 permanent;
}

server {
    listen          80;
    server_name     domain.com;
    access_log      /var/log/nginx/domain.com/access.log;
    error_log       /var/log/nginx/domain.com/error.log;
    rewrite_log     on;
    root            /var/www/domain.com/current/public; # capistrano son versiyonu current klasörüne linklediği için current kullanıyoruz
    index           index.php;

location / {
        try_files $uri $uri/ /index.php?$query_string; # pagination ya da herhangi bir $_GET yöntemine izin veriyoruz
    }

    if (!-d $request_filename) {
        rewrite ^/(.+)/$ /$1 permanent;
    }

    location ~* \.php$ {
fastcgi_pass                    unix:/run/php/php7.1-fpm.sock;
        fastcgi_index                   index.php;
        fastcgi_split_path_info         ^(.+\.php)(.*)$;
        include                         /etc/nginx/fastcgi_params;
        fastcgi_param                   SCRIPT_FILENAME $document_root$fastcgi_script_name;
    } 

    # apache sunucusunda olmadığımız için htaccess'e ihtiyacımız yok

    location ~ /\.ht {
        deny all;
    }
}
php7.1-fpm konfigürasyonunu düzenlemek için alttaki komutu yazın.
nano /etc/php/7.1/fpm/pool.d/www.conf
Konfigürasyon alttaki gibi olmalı.
user  = deployer
group = www-data

listen = /run/php/php7.1-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0666
Son olarak da deployer kullanıcısı için, sadece service php7.1-fpm restart komutu için, şifre yazmasına gerek kalmadan sudo kullanabilmesine izni vereceğiz, böylece her yeni deployment'tan sonra kısa bir süreliğine no input file specified hatası almayacaksınız. Alttaki komutu yazın, # işaretinden sonraki gibi, kullanıcınızın izinlerini düzenleyin.
visudo
Alttaki satırı ekleyin.
deployer ALL=(ALL) NOPASSWD: /usr/sbin/service php7.1-fpm restart
Eğer, deployment yapacağınız kullanıcının adı deployer ise, sırayla alttaki düzenlemeleri yapmanız, eğer bunu yapmazsanız, deployment konusunda sürekli permission denied hatası alırsınız. Ubuntu'daki default web grubu olan www-data için deployer kullanıcısını ekleyin.
$ sudo usermod -a -G www-data deployer
Deployment'ı yapacağımzı /var/www klasörünü www-data grubuna atayıp, bu gruba yazma izni verin.
$ chown -R root:www-data /var/www
$ chmod -R 0775 /var/www
Son olarak /var/www klasöründe oluşacak olan her yeni ve klasörü de www-data grubu tarafından sahiplenilmesi için alttaki komutu uygulayın.
$ find /var/www -type d -exec chmod 2775 {} +;    
Dosya ve klasörlerin, okuma ve yazma izinlerini de alttaki gibi düzenlemeniz lazım.
$ find /var/www -type f -exec chmod 0664 {} +;
Bu kısımla ilgili son düzenleme olarak da nginx ve php7.1-fpm'i tekrar başlatın.
$ service php7.1-fpm restart
$ service nginx restart
Sunucu ile bir işimiz daha var, public repository'ler ile uğraşırken sunucunun public key'ine ihtiyacımız olmasa da, private repository'ler için bu durum geçerli değil. Ya bir kullanıcı adı ve şifre sunmak zorundasınız, ya da sunucu için geçerli bir SSH key oluşturmalısınız ki kullanıcı adı ve şifre sunmak kötü bir tercih. Bitbucket veya Github üzerinde bir private repository'niz olduğunu varsayarak, anlatıma devam edeceğim.

deployer hesabınızla, sunucuda,
$ ssh-keygen -t rsa # enter ile geçin ve passphrase(parola)'nizi yazın.
Sonrasında basit bir cat komutu ile, public key'i ekran yazdırın ve çıktıyı kopyalayın, daha sonrasında, Bitbucket veya Github'ta bulunan projenizin ayarlar(Settings) kısmındaki Deployment Keys kısmından Add Key'e basıp, bu anahatarı ekleyin.
$ cat /home/deployer/.ssh/id_rsa.pub 
Daha sonra Github ya Bitbucket repository'nizi known hosts(güvenilen hostlara)'a eklemeniz lazım, her ikisinin örneği de alttaki gibi. Bu komutlardan birini yazdıktan sonra sizden biraz önce yazmış olduğunuz passphrase'i girmenizi isteyecek.
$ git ls-remote -h git@bitbucket.org:KULLANICIADI/PROJE.git HEAD # bitbucket
$ git ls-remote -h git@github.com:KULLANICIADI/PROJE.git HEAD # ya da Github
Ancak işlem bunlarla bitmiyor, deployer hesabı sunucuya SSH ile her bağlandığında ssh-agent çalışmıyorsa, doğrulama gerçekleşmeyecek. Dolayısıyla her yeni oturum başlangıcında, ssh-agent'ı başlatıp, passphrase'i eklememiz lazım. Ancak burada da keychain eklentisi ile bu zorluğu aşabiliriz ki en baştaki install komutunda dahil olması lazım, değilse de sudo apt-get install keychain ile yükleyebilirsiniz. Sunucunuzda
$ nano ~/.bash_profile
Profil dosyasının içerisine alttaki komutu yapıştırın.
eval `keychain --eval --agents ssh id_rsa`
Daha sonra exit ile sunucudan çıkış yapın ve sunucunuza tekrar giriş yapın. Keychain'in sizden passphrase istediğini göreceksiniz, onu tekrar yazın.

Buradan sonrasıyla keychain ilgilenicek, siz çıkış yapsanız bile, ssh-agent'ın çalışmasını sağlayacak ve passphrase'i de hatırlayacağı için, tekrar tekrar bunu girmenize gerek kalmayacak.

Son aşamamız ise, deploymentı nasıl yapacağınız. Capistrano'yu kurabilmeniz için, kendi bilgisayarınızda Ruby'nin yüklü olması lazım. 5-10 dakika arası bir kurulum süresi var.
$ sudo apt-get update
$ sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev

$ cd ~
$ git clone git://github.com/sstephenson/rbenv.git .rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
$ exec $SHELL
$ git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
$ echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
$ exec $SHELL

$ git clone https://github.com/sstephenson/rbenv-gem-rehash.git ~/.rbenv/plugins/rbenv-gem-rehash
$ rbenv install 2.2.4
$ rbenv global 2.2.4
$ ruby -v # bir çıktı verirse, ruby'yi başarıyla kurmuşsunuzdur demektir.

$ echo "gem: --no-ri --no-rdoc" > ~/.gemrc
$ gem install bundler
$ gem install capistrano
$ gem install capistrano-file-permissions
Bu uzun yüklemeden sonra, Laravel projenizin içerisinde Capistrano direktifini oluşturmanız lazım. Bunun için projenin root(ana) dizininde
$ cap install
Öncelikli olarak Capfile dosyasında neleri kullanacağımızı belirtin.
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/file-permissions'

Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
Daha sonra config klasöründe, deploy klasörü ile deploy.rb dosyalarının oluştuğunu göreceksiniz. Deploy klasörü içerisindeki kısımlar, geliştirme ortamını belirtmek için kullanılıyor.

Deploy klasörü içerisindeki, production.rb dosyasını minimum konfigürasyonla alttaki gibi oluşturabilirsiniz.
server 'SUNUCU_IP_ADRESI',
user: 'deployer',
roles: %w{web db app},
ssh_options: {
user: 'deployer',
  keys: %w(/home/BILGISAYARINIZDAKI_KULLANICI_ADINIZ/.ssh/id_rsa),
  forward_agent: true,
  auth_methods: %w(publickey)
}
deploy.rb dosyasını da alttaki gibi oluşturabilirsiniz. O biraz daha karışık gibi görünebilir ama aslında gayet basit, alttak gibi bir konfigürasyon kullanabilirsiniz, bazılarının açıklamalarını yanına yazdım.
set :application, 'domain.com'
set :repo_url, 'git@bitbucket.org:KULLANICIADI/PROJE.git' # ya da -> 'git@github.com:KULLANICIADI/PROJE.git'
set :deploy_to, '/var/www/domain.com'
set :keep_releases, 3
set :scm, :git
set :log_level, Logger::DEBUG
set :default_stage, "production"

set :linked_dirs, fetch(:linked_dirs, []) + %w{public/uploads} # her bir sürümde, paylaşılacak olan dosyalar, örneğin kullanıcıların yüklediği dosyalar

namespace :php7_fpm do
    desc "php7.1-fpm yeniden baslatir"
    task :restart do
        on roles(:all) do
            execute :sudo, :service, "php7.1-fpm", "restart"
        end
    end
end

namespace :composer do
    desc "Yukleme dizininde composer install calistirir"
    task :install do
        on roles(:all) do
            within release_path do
                execute "composer", "install", "--no-dev", "--no-interaction", "--prefer-dist"
            end
        end
    end
end

namespace :laravel do

    desc "Laravel Artisan migrate."
    task :migrate do
        on roles(:all) do
            within release_path do
                execute "php", "artisan", "migrate"
            end
        end
    end

    desc "Gelistirme ortami dosyalarini temizler"
    task :cleanup do
        on roles(:all) do
            within release_path do
              execute "rm", "bower.json"
              execute "rm", "composer*"
              execute "rm", "gulpfile.js"
              execute "rm", "LICENSE"
              execute "rm", "package.json"
              execute "rm", "phpunit.xml"
              execute "rm", "README.md"
              execute "rm", "server.php"
            end
        end
    end
end

namespace :deploy do
    after :publishing, "composer:install"
    after :publishing, "laravel:migrate"
    after :finishing,  "laravel:cleanup"
    after :finishing,  "deploy:cleanup"
    after :finishing, "php7_fpm:restart"
    after :rollback,  "php7_fpm:restart"
end
Dosyaları bu şekilde düzenledikten sonra, projenizi sunucuya aktarmak için yapmanız gereken tek şey, production ortamı için,
cap production deploy
Tüm olası komutlara bakmak için
cap -T 
Kısacası, Nginx kurulumundan ya da Capistrano vb. modern araçlarla deployment(aktarım) yapmaktan korkmayın, projelerinizi güvenli ve efektif şekilde yönetin ve herhangi bir dosyaya şifre yazmayın.