Deploying WordPress – Part 2: Deploying WordPress with Capistrano

Deploying WordPress – Part 2: Deploying WordPress with Capistrano

Capistrano

ในตอนที่แล้วได้พูดถึงการจัดการซอร์ซโค้ดของ WordPress ด้วย Git และในตอนท้ายได้พูดถึงการ deploy WordPress โดยใช้ Git hook เอาไว้เล็กน้อย ลำพังตัว Git เองสามารถนำมาประยุกต์ใช้เพื่อ deploy WordPress ได้ก็จริง แต่ก็มีข้อจำกัดค่อนข้างมาก เช่นไม่สามารถเลือกได้ว่าจะ deploy ไปที่เซิร์ฟเวอร์ตัวไหน ไม่สามารถ rollback กลับไปที่รีลีสก่อนหน้าในกรณีเกิดปัญหาที่ไม่คาดคิดเป็นต้น การใช้ Capistrano เข้ามาช่วยในการะบวนการ deploy จะช่วยทำให้การ deploy ง่ายขึ้น และมีความยืดหยุ่นมากขึ้น โดยที่เราสามารถแทรกซอร์ซโค้ดที่ต้องการ พัฒนาส่วนขยายเพิ่มเติม หรือใช้ส่วนขยายที่มีคนพัฒนาเอาไว้แล้วก็ได้ Capistrano เป็นเครื่องมือที่ใช้ในการช่วย deploy ซอร์ซโค้ดของแอปพลิเคชันไปที่เซิร์ฟเวอร์ ซึ่งเดิมทีพัฒนามาเพื่อใช้กับแอปพลิเคชันที่พัฒนาด้วย Ruby on Rails แต่ก็สามารถเอามาประยุกต์ใช้กับแอปพลิเคชันที่พัฒนาด้วยภาษาอื่นๆได้

การทำงานของ Capistrano

 

capistrano

 

Capistrano จะถูกติดตั้งอยู่ในเครื่องของนักพัฒนา (Dev) ซึ่งใช้ Git เป็นซอฟต์แวร์ในการควบคุมรุ่นของซอฟต์แวร์ ซึ่งเราใช้ในการควบคุมรุ่นของ WordPress ซึ่งมี remote repository อยู่ที่ github การใช้งาน Capistrano จะทำผ่านคำสั่ง cap ทาง command line เมื่อเราใช้คำสั่ง cap โปรแกรม Capistrano จะเข้าไปอ่านไฟล์คอนฟิกเกอเรชั่นในไดเรกทอรี config ซึ่งอยู่ภายใต้โปรเจค (mysite) จากนั้นจะทำ remote execution ผ่านทาง ssh ไปที่เซิร์ฟเวอร์ production เพื่อใช้คำสั่ง git ในการดึงซอร์ซโค้ดจาก github มาไว้ที่เซิร์ฟเวอร์ production โดยจะเก็บซอร์ซโค้ดของการ deploy แต่ละครั้งแยกออกจากกันและใช้ symbolic link ชื่อว่า current เพื่อชี้ไปที่ซอร์ซโค้ดที่ถูก deploy ครั้งล่าสุดเสมอ

ด้วยวิธีการนี้ทำให้เราสามารถ rollback ไปที่การ deploy ก่อนหน้าได้ นอกจากนั้นยังอาศัยความสามารถของการทำ remote execution ผ่าน ssh ในการทำงานหลายๆอย่างบนเซิร์ฟเวอร์  เช่น restart  deploy ฐานข้อมูล MySQL ทำการสำรองข้อมูล และสร้างไฟล์คอนฟิกเกอเรชั่นสำหรับ WordPress ที่เหมาะสมสำหรับใช้ในจุดประสงค์ที่ต่างกันได้

ยกตัวอย่างวิธีการใช้งานคร่าวๆเพื่อให้เห็นภาพชัดเจนขึ้น เช่นถ้าเราต้องการที่จะ deploy development branch ของ repo ไปที่เซิร์ฟเวอร์สามารถทำโดยใช้คำสั่ง

$ cap deploy

ถ้าเราต้องการ rollback แอปพลิเคชันจากรีลีสปัจจุบันสู่รีลีสก่อนหน้า สามารถทำโดยใช้คำสั่ง

$ cap deploy:rollback

ในขณะที่เขียนบล็อกนี้ Capistrano ได้ออกเวอร์ชัน 3 แล้วตั้งแต่เดือนมิถุนายน 2556 ซึ่งจะมีความแตกต่างจากเวอร์ชัน 2 อยู่พอสมควร และถึงแม้ว่า Capistrano เวอร์ชัน 3 จะมีความสามารถมากขึ้น แต่ยังพบปัญหาในการใช้งานอยู่บ้างในบางกรณี เช่นยังไม่รองรับ git module ซึ่งเราใช้ในการจัดการซอร์ซโค้ดของ WordPress อยู่พอดี ดังนั้นเพื่อไม่ให้เกิดปัญหาในบล็อกนี้จะอ้างอิงถึง Capistrano เวอร์ชัน 2 เท่านั้น

เครื่องนักพัฒนา

เราจะเริ่มจากการติดตั้ง Capistrano ที่เครื่องนักพัฒนา โดยเราจะใช้ Capistrano เวอร์ชัน 2.15.5

ก่อนที่จะติดตั้ง Capistrano เราต้องทำการติดตั้ง RubyGems เวอร์ชันอย่างน้อย 1.3.x สำหรับผู้ที่ใช้ Ubuntu Linux สามารถศึกษาวิธีการติดตั้งได้จาก RubyOnRails @Ubuntu documentation ใน Capistrano เวอร์ชัน 2 เราต้องทำการติดตั้ง gem 3 แพ็กเกจดังนี้

nontster@dev:~$ sudo gem install capistrano -v 2.15.5
nontster@dev:~$ sudo gem install capistrano-ext
nontster@dev:~$ sudo gem install railsless-deploy

capistrano-ext เป็นส่วนขยายของ Capistrano ซึ่งทำให้สามารถใช้ได้กับการ deploy หลายๆที่ เช่นใน เซิร์ฟเวอร์ production และ เซิร์ฟเวอร์ staging เป็นต้น  railsless-deploy จะเป็นตัวที่ทำให้สามารถใช้ capistrano กับแอปพลิเคชันอื่นๆที่ไม่ได้พัฒนาด้วย Ruby on Rails ได้

Authentication & Authorisation

ในการใช้งาน Capistrano จะต้องมีการเชื่อมต่อเข้าไปที่เซิร์ฟเวอร์ที่ต้องการ deploy ผ่านโปรโตคอล ssh โดยไม่ต้องทำการใส่รหัสผ่าน ซึ่งจะต้องมีการตั้งค่าให้กับ ssh ในฝั่งของเครื่องนักพัฒนาตามขั้นตอนต่อไปนี้

ขั้นที่ 1: ตรวจสอบว่ามีกุญแจ SSH อยู่แล้วหรือไม่

เปิดโปรแกรม terminal แล้วเข้าไปใน home ไดเรกทอรี ของผู้ใช้งาน จากนั้นให้ใส่คำสั่งดังนี้

nontster@dev:~$ cd ~/.ssh
nontster@dev:~$ ls -al

ตรวจดูว่ามีไฟล์ที่ใช้ชื่อไฟล์ว่า id_rsa.pub หรือ id_dsa.pub อยู่หรือไม่ ถ้าหากไม่พบให้ทำตามในขั้นตอนที่ 2: แต่ถ้าหากมีให้ข้ามไปทำในขั้นตอนที่ 3:

ขั้นที่ 2: สร้างกุญแจ SSH

nontster@dev:~$ ssh-keygen -t rsa -C 'your_email@example.com'
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/you/.ssh/id_rsa): [กด enter]

จากนั้นระบบจะให้ใส่ passphrase

Enter passphrase (empty for no passphrase): [ใส่ passphrase]
Enter same passphrase again: [ใส่ passphrase อีกครั้ง]

เมื่อใส่ passphrase ตรงกันทั้งสองครั้ง กุญแจจะถูกสร้างขึ้น

Your identification has been saved in /Users/you/.ssh/id_rsa.
Your public key has been saved in /Users/you/.ssh/id_rsa.pub.
The key fingerprint is:
12:c1:98:79:39:43:f1:5b:64:e9:b2:f0:e5:12:8d:b1 your_email@example.com

จากนั้นเพิ่มกุญแจที่เพิ่งสร้างขึ้นเข้าไปใน ssh-agent:

nontster@dev:~$ ssh-add ~/.ssh/id_rsa

ขั้นที่ 3: เพิ่มกุญแจ SSH ที่สร้างขึ้น เข้าไปใน GitHub

ใช้คำสั่งต่อไปนี้เพื่อคัดลอกข้อมูลของกุญแจ SSH เข้าไปใน clipboard คำสั่ง pbcopy มีใน OS X สำหรับ Ubuntu ให้ใช้โปรแกรม xclip แทน หรือาจใช้คำสั่ง cat เพื่อแสดงข้อมูลของกุญแจ SSH ใน terminal แล้วใช้เมาส์ลากเพื่อคัดลอกข้อมูลของกุญแจ SSH เข้าไปใน clipboard ตรงๆเลยก็ได้ แต่ต้องระวังอย่าให้มี whitespace หัวท้ายข้อมูล

nontster@dev:~$ pbcopy < ~/.ssh/id_rsa.pub
# Copies the contents of the id_rsa.pub file to your clipboard

ไปที่เว็บ GitHub และ ล็อกอินเข้าไปที่บัญชีของเรา

  1. ที่มุมขวาบนของ user bar  คลิก Account Settings. userbar-account-settings
  2. คลิก SSH Keys ที่ sidebar ด้านซ้าย settings-sidebar-ssh-keys
  3. คลิก Add SSH key ssh-add-ssh-key
  4. ในฟิลล์ Title ให้ใส่คำอธิบายสั้นๆเกี่ยวกับกุญแจเพื่อให้จำได้ง่ายว่าเป็นกุญแจของอะไร เช่นจากตัวอย่างผู้เขียนใช้แล็ปท็อป Macbook Pro ก็อาจจะตั้งชื่อกุญแจว่า “My Macbook Pro”
  5. จากนั้นให้ Paste ข้อมูลกุญแจจาก clipboard เข้าไปที่ฟิลล์ “Key” ssh-key-paste
  6. คลิก Add key. The Add key button
  7. จะเห็นว่ากุญแจถูกเพิ่มเข้ามาเรียบร้อยแล้ว github_keys_before

ขั้นที่ 4: ทดสอบกุญแจ SSH

ในขั้นตอนนี้จะเป็นการทดสอบกุญแจ SSH ที่เพิ่งใส่เข้าไปว่าสามารถใช้งานได้หรือไม่โดยการ  ssh ไปที่ GitHub โดยในขั้นตอนนี้เราจะต้องใส่ passphrase ที่เราได้สร้างไว้ตั้งแต่ขั้นตอนที่สร้างกุญแจ SSH เริ่มด้วยการเปิดโปรแกรม Terminal และพิมพ์คำสั่ง:

nontster@dev:~$ ssh -T git@github.com

จากนั้นใส่ passphrase ssh_passphase ถ้าหาก passphrase ถูกต้องจะพบข้อความดังนี้

Hi nontster! You’ve successfully authenticated, but GitHub does not provide shell access.

เมื่อกลับไปที่หน้าเว็บของ GitHub จะพบว่ากุญแจถูกเปิดใช้งานเรียบร้อยแล้ว github_keys_after สามารถหาข้อมูลเพิ่มเติมได้จาก Generating SSH Keys

เครื่องเซิร์ฟเวอร์ production

ในส่วนของเครื่องเซิร์ฟเวอร์ production ให้ติดตั้ง ssh server และ LAMP stack ซึ่งถ้าใช้ Ubuntu ก็สามารถศึกษาวิธีการติดตั้งได้จาก How to install LAMP on Ubuntu ติดตั้งโปรแกรม Git โดยใช้คำสั่ง

root@production:~# apt-get install git-core

จากนั้นให้เพิ่มบัญชีผู้ใช้งานโดยตั้งชื่อบัญชีว่า deployer และให้อยู่ใน group deployer เช่นเดียวกัน

root@production:~# addgroup deployer; adduser --system --shell /bin/bash --ingroup deployer --disabled-password --home /home/deployer deployer

สร้างไดเรกทอรีและตั้งค่าสิทธิ์ในการเข้าถึงสำหรับเว็บเซิร์ฟเวอร์

root@production:~# mkdir -p /var/www
root@production:~# usermod -a -G www-data deployer
root@production:~# chgrp -R www-data /var/www
root@production:~# umask 002
root@production:~# chmod g+s /var/www
root@production:~# chmod -R 775 /var/www

ตั้งค่าของ DocumentRoot ใน Apache ให้สอดคล้องกับไดเรกทอรีของ WordPress โดยแก้ไขไฟล์ 000-default.conf

root@production:~# vim /etc/apache2/sites-enabled/000-default.conf

เปลี่ยนค่า DocumentRoot จาก /var/www/html เป็น /var/www/mysite/current

ตั้งค่าเพื่อทำ remote login และ remote execution

ในขั้นตอนนี้จะทำการตั้งค่าในเครื่องเซิร์ฟเวอร์ production เพื่อให้เครื่องของนักพัฒนาสามารถทำ remote login และ remote execution ไปที่เครื่องเซิร์ฟเวอร์ production ได้ ในเครื่องเซิร์ฟเวอร์ production เริ่มจากทำการสลับบัญชีผู้ใช้งานจาก root เป็น deployer โดยใช้คำสั้ง

root@production:~# su - deployer

สร้างไดเรกทอรี .ssh ใน ไดเรกทอรี home ของผู้ใช้ deployer

deployer@production:~$ mkdir -p ~/.ssh

นำค่าที่ได้จาก clipboard จากการใช้คำสั่ง pbcopy ขั้นตอนที่ 3 “เพิ่มกุญแจ SSH ที่สร้างขึ้น เข้าไปใน GitHub” ซึ่งได้กล่าวถึงข้างต้น มาใส่ในไฟล์ authorized_keys ของ deployer โดยใช้คำสั่ง เมื่อ Paste key จาก clipboard แล้วจะมีลักษณะดังนี้

deployer@production:~$ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCx95O9IUugvbF91uhaQ3xz2foCSVVnYZuN4oyT4Vg1Ba1ROH4VlbcSa20uysuDkPkZJOf701Hy0fj/wMN1lYyMUGr76x99/D7ZvaXHWE9TyzK9kVX79mP+bctWbuP+u/SjDV1Wttc8gPTH70oWmdEieCEZPiwySyhlL/GQBw43yjFmKwEhdzu7yywME5KaVp+Z4kkufc4CYupKNopN9f31NWnpO0FtZTtyF5y07uweuwj3gb33Nd6GFEoVtzqs9rpwTQhoAq3rd44sC53Pf18cnjmcYs/vwc7B7hrLKMBSJrwtWSYTKyiBjxJtjvbdi0lQLPqP64XzvIHdMFssOmUV your_email@example.com" >> .ssh/authorized_keys

ทำการเซ็ท permission ของไดเรกทอรี .ssh และไฟล์ authorized_keys

deployer@production:~$ chmod 700 .ssh
deployer@production:~$ chmod 600 .ssh/authorized_keys

ทดลองทำ remote execution จากเครื่องนักพัฒนาไปที่เครื่องเซิร์ฟเวอร์ production

nontster@dev:~$ ssh deployer@[IP ของเครื่องเซิร์ฟเวอร์ production] 'hostname; uptime'
เช่น
nontster@dev:~$ ssh deployer@xxx.xxx.xxx.xxx 'hostname; uptime'
production
 14:03:01 up 48 min,  0 users,  load average: 0.17, 0.06, 0.06

ถ้าหากได้ผลเป็นชื่อเซิร์ฟเวอร์ และค่าของ load average แสดงว่าสำมารถทำ remote execution โดยไม่ต้องใส่รหัสผ่านได้แล้ว จากนั้นลองใช้คำสั่งด้านล่างเพื่อทดสอบการทำ remote execution จากเครื่องนักพัฒนาและใช้ Git จากเครื่องเซิร์ฟเวอร์ production เพื่อไปดึงข้อมูลจาก GitHub อีกต่อหนึ่ง ถ้าหากไม่มี error อะไรแสดงว่าสามารถเชื่อมต่อและดึงข้อมูลจาก GitHub ได้สำเร็จ

nontster@dev:~$ ssh -A deployer@xxx.xxx.xxx.xxx 'git ls-remote git://github.com/WordPress/wordpress.git'

c621255236a826222dedbea4dedebfef4d468910 HEAD
bc050faba822d11d74771030ae2e5c2df215810b refs/heads/1.5-branch
62fae847b265a58290b3d399cba5db73baf633fa refs/heads/2.0-branch
...

การเตรียมโปรเจคเพื่อใช้กับ Capistrano

ซอร์ซโค้ดทั้งหมดจะอยู่ที่เครื่องของนักพัฒนา อ้างอิงถึงบล็อกในตอนที่ 1 ให้เข้าไปในไดเรกทอรีโปรเจคที่เก็บซอร์ซโค้ด

nontster@dev:~$ cd mysite

พิมพ์คำสั่ง capify . เพื่อติดตั้ง Capistrano เข้าไปในโปรเจค

nontster@dev:~$ capify .
[add] writing './Capfile'
[add] making directory './config'
[add] writing './config/deploy.rb'
[done] capified!

จะมีไฟล์สองไฟล์กับอีกหนึ่งไดเรอทอรีถูกสร้างขึ้น ได้แก่ไฟล์ชื่อ Capfile ทำหน้าที่เป็นไฟล์หลักที่เก็บขั้นตอนของการ deploy ทุกอย่าง คล้ายๆ Makefile ในภาษา C หรือ rakefile ในภาษา Ruby  เมื่อใช้คำสั่ง cap ตัว Capistrano  จะทำการอ่าน Capfile เป็นไฟล์แรก เมื่อ Capfile ถูกสร้างขึ้นมาจะมีค่าตั้งต้นดังโค้ดด้านล่าง

load 'deploy'
# Uncomment if you are using Rails' asset pipeline
# load 'deploy/assets'
load 'config/deploy' # remove this line to skip loading any of the default tasks

เนื่องจากเราไม่ได้ใช้ Capistrano กับ Rails ให้ลบข้อมูลทิ้งทั้งหมดแล้วพิมพ์โค้ดด้านล่างเข้าไปแทน

require 'railsless-deploy'
load 'config/deploy'

นอกจาก Capistrano จะสร้างไฟล์ชื่อ Capfile แล้ว ยังสร้างไดเรกทอรี config และไฟล์ deploy.rb ภายใต้ไดเรอทอรี config อีกด้วย คำสั่ง load ‘config/deploy’ ใน Capfile จะเป็นการบอก Capistrano ว่าให้ไปโหลดเอาขั้นตอนการ deploy มาจากไฟล์ deploy.rb อีกต่อหนึ่ง


############################################
# Setup Stages
############################################
server "localhost", :app

############################################
# Setup Git
############################################
set :application, "mysite"
set :repository, "git@github.com:nontster/mysite.git"
set :scm, :git
set :deploy_to, "/var/www/#{application}"
set :user, "deployer"
set :copy_exclude, [".git", ".DS_Store", ".gitignore", ".gitmodules"]
set :deploy_via, :remote_cache
set :git_enable_submodules, 1
set :default_run_options, {:pty => true}

############################################
# Setup Server
############################################
set :ssh_options, { :forward_agent => true }
set :use_sudo, false
ssh_options[:port] = 2222
  • server “production.example.com”, :app กำหนดชื่อของเซิร์ฟเวอร์ที่เราต้องการ deploy
  • set :application, “my site” กำหนดค่าคงที่ให้กับตัวแปร application ซึ่งเราใช้ตัวแปรนี้สำหรับเก็บชื่อแอปพลิเคชัน
  • set :repository, “git@github.com:nontster/mysite.git” กำหนด URL ของ repository ที่เราต้องการนำเอาซอร์ซโค้ดมาใช้ในการ deploy
  • set :scm, :git บอก Capistrano ว่าใช้ git repository
  • set :deploy_to, “/var/www/#{application}” กำหนดชื่อไดเรกทอรีในเซิร์ฟเวอร์ที่เราต้องการ deploy ซอร์ซโค้ดเข้าไป
  • set :user, “deployer” กำหนดชื่อบัญชีผู้ใช้สำหรับใช้ใน SSH
  • set :copy_exclude, [“.git”, “.DS_Store”, “.gitignore”, “.gitmodules”]  บอก Capistrano ว่าไม่ต้องการคัดลอกไฟล์เหล่านี้เข้าไปใน เซิร์ฟเวอร์ตอนที่ deploy
  • set :deploy_via, :remote_cache บอกให้ Capistrano เก็บแคชของ repository ไว้บนเซิร์ฟเวอร์ที่เราต้องการ deploy ด้วยเพื่อความรวดเร็วในการ deploy ครั้งต่อๆไป
  • set :git_enable_submodules, 1  บอกให้ Capistrano ทำการดึงซอร์ซโค้ดของ git submodule เข้ามาด้วย (อ้างอิงจากบล็อกตอนที่แล้วเราใช้ wordpress เป็น submodule)
  • set :default_run_options, {:pty => true} บอก Capistrano ให้ใช้ pseudo terminal เพื่อให้ทำงานได้เวลา git ถามรหัสผ่าน
  • set :ssh_options, { :forward_agent => true }  บอกให้ Capistrano ใช้ private key ของเครื่องนักพัฒนาสำหรับใช้กับ git repository แทนที่จะเอาจากเครื่องเซิร์ฟเวอร์ สามารถศึกษาเพิ่มเติมได้จาก SSH agent forwarding
  • set :use_sudo, false บอกให้ Capistrano ไม่ต้องใช้คำสั่ง sudo ในขั้นตอนการ setup
  • ssh_options[:port] = 2222 กำหนดพอร์ทของ SSH บนเครื่องเซิร์ฟเวอร์ ในกรณีที่ไม่ได้ใช้พอร์ทมาตรฐาน (พอร์ท 22)

โครงสร้างไดเรกทอรีของ Capistrano ในฝั่งเซิร์ฟเวอร์

ก่อนที่จะทำการ deploy เรามาทำความเข้าใจโครงสร้างไดเรกทอรีของ Capistrano ในฝั่งเซิร์ฟเวอร์กันก่อน ในการ deploy ครั้งแรกเราต้องสั่งให้ Capistrano ทำการสร้างไดเรกทอรีที่จำเป็นโดยใช้คำสั่ง cap deploy:setup ซึ่ง Capistrano จะทำการสร้างไดเรกทอรีตามที่ระบุไว้ในตัวแปร deploy_to

 nontster@dev:~$ cap deploy:setup
  * 2014-04-29 19:44:16 executing `deploy:setup'
  * executing "mkdir -p /var/www/mysite /var/www/mysite/releases /var/www/mysite/shared &&  chmod g+w /var/www/mysite /var/www/mysite/releases /var/www/mysite/shared"
    servers: ["localhost"]
    [localhost] executing command
    command finished in 1744ms

ในบล็อกนี้เราใช้ไดเรกทอรี /var/www/mysite ภายใต้ไดเรกทอรี mysite จะมีไดเรกทอรี releases ไดเรกทอรี shared และ symbolic link ชื่อว่า current ซึ่งชี้ไปที่ไดเรกทอรีที่มีการ deploy ล่าสุดภายใต้ไดเรกทอรี releases

ภายใต้ไดเรกทอรี releases จะเป็นไดเรกทอรีสำหรับเก็บซอร์ซโค้ดของการ deploy ในแต่ละครั้ง โดยชื่อไดเรกทอรีอยู่ในรูปแบบของวันเวลาที่สั่ง deploy ดังตัวอย่าง

deployer@production:~$ ls -l /var/www/mysite/releases
total 12
drwxrwsr-x 4 deployer www-data 4096 Apr 29 19:48 20140429124858
drwxrwsr-x 4 deployer www-data 4096 Apr 29 19:58 20140429125800
drwxrwsr-x 4 deployer www-data 4096 Apr 29 19:58 20140429125828
deployer@production:~$ ls -l /var/www/mysite/releases/20140429124858
total 24
-rw-rw-r-- 1 deployer www-data 428 Apr 29 19:44 index.php
-rw-rw-r-- 1 deployer www-data 24 Apr 29 19:44 README.md
-rw-rw-r-- 1 deployer www-data 41 Apr 29 19:48 REVISION
drwxrwsr-x 5 deployer www-data 4096 Apr 29 19:48 wordpress
-rw-rw-r-- 1 deployer www-data 3681 Apr 29 19:44 wp-config.php
drwxrwsr-x 4 deployer www-data 4096 Apr 29 19:44 wp-content

Capistrano ใช้ไดเรกทอรีชื่อว่า shared สำหรับข้อมูลที่ไม่เปลี่ยนแปลงระหว่าง release ยกตัวอย่างเช่นไฟล์คอนฟิกเกอเรชั่น และไดเรกทอรีที่มีไว้สำหรับเก็บรูปภาพที่ผู้ใช้อัพโหลดใน wordpress เป็นต้น นอกจากนั้นยังมีไดเรกทอรีชื่อ cached-copy ซึ่งไว้สำหรับเก็บไฟล์แคชของ Git ที่เกิดจากการระบุในตัวแปร deploy_via ให้เป็น remote_cache

deployer@production:~$ ls -l /var/www/mysite/shared/
total 4
drwxrwsr-x 5 deployer www-data 4096 Apr 29 19:44 cached-copy

หลังจากเราใช้คำสั่ง cap deploy:setup แล้วเราสามารถเริ่มใช้คำสั่ง cap deploy เพื่อทำการ deploy ซอร์ซโค้ดได้ดังตัวอย่าง

deployer@production:~$ cap deploy

ถ้าหากเป็นการ deploy static web เราสามารถตั้งค่า Document Root ของเว็บเซิร์ฟเวอร์ให้ชี้มาที่ไดเรกทอรี /var/www/mysite/current ก็จะสามารถใช้งานเว็บได้ทันที แต่ในกรณีของ WordPress จะต้องมีการตั้งค่าในไฟล์คอนฟิกเกอเรชั่น ของ WordPress และทำ symbolic link ระหว่างไดเร็กทอรีและไฟล์ที่เกี่ยวข้องต่างๆถึงจะใช้งาน WordPress ได้ ดังตัวอย่างไฟล์คอนฟิกเกอเรชั่น ด้านล่าง ได้เพิ่มส่วนของคอนฟิกเกอเรชั่น ที่เกี่ยวกับ WordPress เข้าไปตั้งแต่บรรทัดที่ 26-70

############################################
# Setup Stages
############################################
server "localhost", :app

############################################
# Setup Git
############################################
set :application, "mysite"
set :repository, "git@github.com:nontster/mysite.git"
set :scm, :git
set :deploy_to, "/var/www/#{application}"
set :user, "deployer"
set :copy_exclude, [".git", ".DS_Store", ".gitignore", ".gitmodules"]
set :deploy_via, :remote_cache
set :git_enable_submodules, 1
set :default_run_options, {:pty => true}

############################################
# Setup Server
############################################
set :ssh_options, { :forward_agent => true }
set :use_sudo, false
ssh_options[:port] = 2222

############################################
# Recipies
############################################
### WordPress
namespace :wp do
	desc "Create files and directories for WordPress environment"
	task :setup, :roles => :app do
		run "mkdir -p #{shared_path}/uploads"

		set(:wp_siteurl, Capistrano::CLI.ui.ask("Site URL: ") )
		set(:wp_dbname, Capistrano::CLI.ui.ask("Database name: ") )
		set(:wp_dbuser, Capistrano::CLI.ui.ask("Database user: ") )
		set(:wp_dbpass, Capistrano::CLI.password_prompt("Database password: ") )
		set(:wp_dbhost, Capistrano::CLI.ui.ask("Database host: ") )

		db_config = ERB.new <<-EOF
<?php
	define('DB_NAME', '#{wp_dbname}');
	define('DB_USER', '#{wp_dbuser}');
	define('DB_PASSWORD', '#{wp_dbpass}');
	define('DB_HOST', '#{wp_dbhost}');
	define('DB_CHARSET', 'utf8');
	define('DB_COLLATE', '');
	define('WPLANG', '');

	$table_prefix  = 'wp_';

	define('WP_HOME','#{wp_siteurl}');
	define('WP_SITEURL','#{wp_siteurl}/wordpress');
	define('UPLOADS',''.'uploads');
?>
    EOF

    	put db_config.result, "#{shared_path}/wp-config-production.php"
	end

	desc "Setup symlinks for a WordPress project"
	task :create_symlinks do
		run "ln -nfs #{shared_path}/uploads #{release_path}/wordpress/uploads"
		run "ln -nfs #{shared_path}/wp-config-production.php #{release_path}/wp-config-production.php"
	end
end

after "deploy:create_symlink", "wp:create_symlinks"
after "deploy:setup", "wp:setup"

จากโค้ดด้านบนเมื่อเราสั่ง cap deploy:setup เมื่อ Capistrano ทำการสร้างไดเรกทอรีที่เกี่ยวข้องเสร็จก็จะทำงานใน task ที่เราเขียนเพิ่มเติมเข้าไปจากที่เรากำหนดในบรรทัดที่ 70

after "deploy:setup", "wp:setup"

เราตั้งชื่อ wp:setup โดยที่ wp เป็นชื่อของ namespace ส่วน setup เป็นชื่อของ task ตามลำดับ

ใน task wp:setup จะทำการสร้างไดเรกทอรี uploads ภายใต้ไดเรกทอรี shared (โค้ดบรรทัดที่ 33)  และจะสร้างไฟล์คอนฟิกเกอเรชั่นของ WordPress (โค้ดบรรทัดที่ 42-56) โดยรับค่าของชื่อฐานข้อมูล ชื่อผู้ใช้สำหรับติดต่อฐานข้อมูล และรหัสผ่าน และชื่อเครื่องของฐานข้อมูลตามลำดับ (โค้ดบรรทัดที่ 35-39) เมธอด Capistrano::CLI.ui.ask และ Capistrano::CLI.password_prompt ของ Capistrano

เมื่อสร้างไฟล์คอนฟิกเกอเรชั่นเสร็จแล้วก็จะส่งไฟล์ไปเก็บไว้ในไดเรกทอรี shared ของเครื่องเซิร์ฟเวอร์ด้วยคำสั่ง put db_config.result และตั้งชื่อไฟล์นี้ว่า wp-config-production.php (โค้ดบรรทัดที่  59)

เมื่อเราสั่ง cap deploy Capistrano ก็จะทำการ deploy ซอร์ซโค้ดของ WordPress ตามปกติ หลังจากที่ Capistrano ทำ task deploy:create_symlink เสร็จก็จะทำ task wp:create_symlinks ที่เราเขียนเพิ่มเติมเข้าไปจากที่เรากำหนดในบรรทัดที่ 69

after "deploy:create_symlink", "wp:create_symlinks"

สรุป

จากตัวอย่างที่กล่าวมาจะเห็นว่า Capistrano ช่วยในกระบวนการการ deploy เว็บแอปพลิเคชันได้มากเนื่องจากมีความยืดหยุ่นสูง โดยเราสามารถแทรกโค้ดเพื่อสั่งให้ Capistrano ช่วยทำงานหลายๆอย่างได้แบบอัตโนมัติ แต่ความสามารถของ Capistrano ยังมีอีกมากซึ่งขึ้นอยู่กับว่าเราจะนำเอาไปประยุกต์ใช้อย่างไรให้เหมาะสมกับงาน เช่นช่วยติดตั้งซอฟต์แวร์ที่จำเป็น และช่วยตั้งค่าที่สอดคล้องกับการ deploy เป็นต้น นอกจากนั้นถ้าหากเราต้องการจะ deploy ซอร์ซโค้ดทันทีหลังจาก push ซอร์ซโค้ดเข้าไปใน remote repository  ก็สามารถทำได้โดยใส่คำสั่ง cap deploy เข้าไปใน git hook ก็ได้

ประโยชน์อีกอย่างของ Capistrano คือเราสามารถช่วยกันพัฒนาโค้ดของการ deploy ซอฟต์แวร์ต่างๆได้รวมถึงใช้ git ในการควบคุมรุ่นของโค้ดที่ใช้ในการ deploy ได้อีกด้วย ซึ่งต่างจากเมื่อก่อนที่เรามักจะจดวิธีการ deploy ไว้ในสมุด หรือในไฟล์ ซึ่งยากที่จะตรวจสอบและติดตามการเปลี่ยนแปลงวิธีการ deploy เหล่านั้น ในชุมชนของนักพัฒนาที่ใช้ Capistrano มีการแบ่งปันโค้ดของการ deploy ซอฟต์แวร์ต่างๆเอาไว้ ถ้าหากลองเสริชคำว่า “Capistrano recipes” ใน google ก็จะพบตัวอย่างอยู่พอสมควร ยกตัวอย่างเช่นการติดตั้ง nginx เป็นต้น

สิ่งที่ขาดในบล็อกนี้คือเรื่องของการตั้งค่าเริ่มต้นให้กับ WordPress หลังจากการ deploy ถ้าหากเคยติดตั้ง WordPress กันมาบ้างจะพบว่าเราต้องเข้าหน้าเว็บที่ติดตั้ง WordPress เพื่อตั้งค่าบางอย่างก่อนจะเริ่มใช้งาน WordPress ได้ กระบวนการนี้สามารถทำผ่านโค้ด PHP และสั่งให้ทำงานโดย Capistrano ก็ได้ แต่ใน WordPress มีเครื่องมือที่ชื่อว่า WP-CLI สามารถช่วยทำงานนี้ได้สะดวกกว่าเพราะต้องไม่ต้องเขียนโค้ด แต่เนื่องจากเราทำการเปลี่ยนโครงสร้างของ WordPress เพื่อที่จะใช้ซอร์ซโค้ดของ WordPress แบบ git module  ซึ่ง WP-CLI ยังไม่เข้าใจโครงสร้างในแบบที่เราพยายามทำ ทำให้ไม่สามารถใช้ WP-CLI กับโครงสร้างของ WordPress ในแบบที่เราใช้อยู่ได้ ดังนั้นทางออกในตอนนี้คือการเขียนโค้ด PHP แล้วเรียกใช้ฟังก์ชันจาก WordPress โดยตรง

ในบล็อกหน้าจะเขียนถึงเรื่อง Role Stage และ Flow ของ Capistrano

Role จะช่วยให้เราสามารถเขียน task ให้ทำงานกับเซิร์ฟเวอร์ที่กำหนดได้ ยกตัวอย่างเช่นเราต้องการ deploy WordPress ไปที่เซิร์ฟเวอร์ในกลุ่มของเซิร์ฟเวอร์ที่ประกอบด้วย เว็บเซิร์ฟเวอร์ เซิร์ฟเวอร์ฐานข้อมูล และโหลดบาลานเซอร์ โดยที่เราต้องเขียนโค้ดเพื่อ deploy ซอร์ซโค้ดของ WordPress ไปที่เว็บเซิร์ฟเวอร์ ทำการสร้างฐานข้อมูลถ้าเป็นการ deploy ครั้งแรกในเซิร์ฟเวอร์ฐานข้อมูล แต่ถ้ามีฐานข้อมูลอยู่แล้วให้ทำการ backup ฐานข้อมูลก่อนทำการ deploy โดยที่ทำในเซิร์ฟเวอร์ฐานข้อมูลที่เป็น master เท่านั้น นอกจากนั้นเราสามารถสั่งโหลดบาลานเซอร์ให้หยุดจ่าย traffic ให้กับเว็บเซิร์ฟเวอร์ที่เรากำลังจะ deploy ซอร์ซโค้ดชั่วคราว เมื่อ deploy เสร็จและมีการตรวจสอบความถูกต้องแล้วจึงสั่งให้โหลดบาลานเซอร์จ่าย traffic เข้ามาได้ตามปกติ และใน Capistrano เราสามารถเขียนโค้ดของการ deploy ให้อยู่ภายใต้ transaction ได้ เมื่อมีความผิดปกติในบางขั้นตอนเราก็สามารถเขียนโค้ดเพื่อทำการ rollback เพื่อให้ระบบกลับไปอยู่ในสถานะที่เหมาะสมได้เช่นกัน

เราสามารถใช้ส่วนขยายของ Capistrano 2.x ที่ชื่อ Multistage เพื่อแยกเซิร์ฟเวอร์ staging และ production ออกจากกัน เราสามารถใช้คำสั่ง cap stagning deploy ในการ deploy เพื่อทำการทดสอบในเซิร์ฟเวอร์ staging  และคำสั่ง cap production deploy เพื่อทำการ deploy เพื่อใช้งานจริงในเซิร์ฟเวอร์ production เป็นต้น

นอกจากนั้นเราควรทำความเข้าใจเรื่องของ Flow ใน Capistrano เพื่อให้เข้าใจเรื่องตำแหน่งของการแทรกโค้ดที่เหมาะสม เช่นเมื่อเราสั่ง cap deploy จะมีงานย่อยๆเกิดขึ้นเบื้องหลังดังนี้


`deploy:update'
`deploy:update_code'
`deploy:finalize_update'
`deploy:create_symlink'

เราสามารถเลือกแทรกโค้ดเข้าไปก่อนที่แต่ละคำสั่งจะทำงาน หรือหลังทำงานก็ได้ นอกจากนั้นเราสามารถ override โค้ดการทำงานของคำสั่งต่างๆได้เช่นกัน ในการ deploy เว็บแอปพลิเคชันที่ไม่ใช่ Rails เราจะใช้ส่วนขยายที่ชื่อว่า Railsless ซึ่ง Flow จะไม่เหมือนกับการ deploy เว็บแอปพลิเคชันที่ใช้ Rails ในการพัฒนา เราสามารถศึกษา Flow ได้โดยตรงจากซอร์ซโค้ดส่วนขยาย Railsless

รายละเอียดของการประยุกต์ใช้งาน Capistrano ยังมีอีกมาก สามารถศึกษาเพิ่มเติมได้จาก Wiki

ใส่ความเห็น

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / เปลี่ยนแปลง )

Twitter picture

You are commenting using your Twitter account. Log Out / เปลี่ยนแปลง )

Facebook photo

You are commenting using your Facebook account. Log Out / เปลี่ยนแปลง )

Google+ photo

You are commenting using your Google+ account. Log Out / เปลี่ยนแปลง )

Connecting to %s