카피스트라노를 이용해 워드프레스 배포 자동화하기

레일스로 개발할 때의 즐거움 중 하나는 Capistrano(GitHub repo)의 간편한 배포 설정 아닐까 싶다. 다행히 Rails app이 아니라도 cap deploy 명령 하나로 방금 작업한 내용을 서버에 설치하는 기쁨을 맛볼 수 있다. 시험삼아 BitBucket에 있는 이 블로그의 소스코드를 서버에 배포하도록 설정해 봤다.

Update: 이 글에 소개한 내용대로 구성된 소스코드를 빗버킷 저장소에 올려두었다.

아래와 같은 상황이라고 가정하고 진행한다.

  1. SSH를 지원하고 Git을 쓸 수 있는 웹 /서버 호스팅.
  2. 워드프레스 코드 전체를 버전관리하고 있다.
  3. 나는 OS X Mountain Lion을 쓰는데, 다른 버전이거나 리눅스라도 거의 비슷할거다.
  4. 로컬에 ruby 그리고 gem이 설치되어 있어야 한다.

참고로, Capistrano는 로컬에 있는 소스코드를 업로드하는 것이 아니라, 원격 저장소에 있는 소스코드를 서버에 내려받는 것을 명심해야 한다. 즉, Git을 사용할 경우 로컬에 커밋하고 배포해봤자 서버에는 반영되지 않는다. 확실하게 푸시한 다음 배포하자.

배포할 프로젝트의 디렉토리 구조를 정하자

특별히 지켜야 하는 디렉토리 구조가 있는건 아니지만, 아래처럼 구성하면 노출되면 안되는 정보가 포함될 수도 있는 Capistrano 설정 파일 등이 웹서버를 통해 노출될 걱정을 안 해도 된다.

./ (project root)
./public/ (WordPress, 웹서버의 document root로 지정할 경로)
./Capfile
./config/

Capfileconfig/deploy.rb는 Capistrano 동작에 꼭 필요한 파일들로, 아래에서 설명할 과정에서 자동으로 만들어진다. Capfile은 make 명령이 사용하는 Makefile이나 rake가 사용하는 Rakefile처럼 cap이라는 명령이 읽어들이는 파일인다는 규칙을 사용한 것 같다. 그런데 하필 config/라는 디렉토리를 사용하는 이유는 뭘까? 아마도 카피스트라노가 Ruby on Rails 프로젝트의 배포를 위해 생겨난 툴이라서 레일스 프로젝트의 디렉토리 규칙을 그대로 사용하기 때문인 것 같다.

Wordpress config files

워드프레스가 로컬의 개발 환경과 실제 운영서버를 알아서 구별하면 참 편할 것이다. 다행히 설정파일 자체가 그리 복잡하지 않기 때문에 wp-config.php 파일을 아래처럼 간단하게 고치기만 해도 꽤 쓸만하다.

$local_config_file = dirname(__FILE__) . '/wp-local-config.php';
if (file_exists( $local_config_file )) {
    require $local_config_file;
    define('WP_DEBUG', true);
    define('WP_DEBUG_DISPLAY', true);
    define('WP_DEBUG_LOG', true);
} else {
    define('DB_NAME', 'wordpress');
    define('DB_USER', 'wordpress');
    define('DB_PASSWORD', 'wordpress');
    define('DB_HOST', 'localhost');
    define('DB_CHARSET', 'utf8');
    define('DB_COLLATE', '');
    define('WP_DEBUG', false);
}

즉, wp-local-config.php라는 파일이 있으면 그 안의 내용을 설정으로 사용하고, 파일이 없으면 운영 서버의 DB 설정을 사용하도록 한다. 경우에 따라 더 정교하게 만들어도 좋겠지만, 일단 전체적인 그림을 그리는 걸로 만족하자.

Git remote repository(BitBucket/GitHub) 설정

서버에서 소스코드를 내려받으려면 최소한 Remote Git repository를 읽을 수 있는 권한이 필요하다. 다행히 BitBucket과 GitHub 모두 deployment key에 서버의 ssh public key(~/.ssh/id_rsa.pub)를 등록하면 저장소의 read permission을 준다.

Capistrano 설치

$ sudo gem install capistrano
$ sudo gem install railsless-deploy

gem[1. http://rubygems.org/]은 PHP의 Pear, Perl의 CPAN과 같은 Ruby의 패키지 관리 도구다. railsless-deploy[1. https://github.com/leehambley/railsless-deploy]는 Capistrano가 기본 장착하고 있는 레일스에 관련된 동작을 제거해준다는데 자세한 내용은 나도 모른다^^

참고로, WordPress, Drupal, Magento 등 여러 PHP CMS를 지원원하는 https://github.com/augustash/capistrano-ash라는 것도 있는데, APC 관리 페이지나 PhpMyAdmin을 자동으로 설치해 주는 등 벼라별 기능을 다 지원하니 관심있는 분은 더 찾아보시라~

Capistrano 설정

$ cd myproject
$ ls
.... public ....
$ capify .
[add] writing './Capfile'
[add] making directory './config'
[add] writing './config/deploy.rb'
[done] capified!

아마도 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

아래처럼 바꾸자.

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

이제 config/deploy.rb를 고칠 차례다.

set :application, "Bar"
set :repository, "git@bitbucket.org:foo/bar.git" # 배포할 소스코드 저장소 주소
set :scm, :git # 원한다면 svn 등 다른 vcs를 쓸 수도 있다.
set :deploy_to, "/home/foo/sites/bar/"
set :deploy_via, :remote_cache # :git_enable_submodule을 1로 설정했다면 이 값을 :copy로 바꾸자 http://stackoverflow.com/a/5561896/157816
set :copy_exclude, [".git", ".DS_Store", ".gitignore", ".gitmodules"]
set :user, "foo" # 서버에 접속할 계정 이름
set :use_sudo, false # root 권한이 필요한 명령은 없을 것이므로.
set :git_shallow_clone, 1
set :git_enable_submodules, 1

role :web, "bar.com" # Your HTTP server, Apache/etc

after "deploy:setup" do
  run "#{try_sudo} mkdir -m 757 -p #{shared_path}/uploads"
end

after "deploy:symlink" do
  run "ln -nfs #{shared_path}/uploads #{release_path}/public/wp-content/uploads"
  run "chmod -R g-w #{release_path}"
end
(자세한 설정은 공식 문서 등을 참고하시되, GitHub의 Deploying with Capistrano가 간편히 보기에 좋다.)

위의 설정을 요약해보면,

  1. bar.com에 ssh로 접속해서
  2. /home/foo/sites/bar/releases에 현재날짜시각으로 새 디렉토리를 만들고 소스코드를 내려받은 다음
  3. /home/foo/sites/bar/current로 방금 만든 디렉토리를 링크하고
  4. /home/foo/sites/bar/current/REVISION에 방금 설치된 Git 저장소의 리비전을 기록한 다음
  5. /home/foo/sites/bar/shared/upload를 /home/foo/sites/bar/current/public/wp-content/uploads로 심볼릭 링크하도록 한다. 여기에 관해서는 다음 단계에서 더 자세히 설명한다.
하지만 아직 배포는 하지 말자. 남은 과정이 좀 더 있다.

배포 준비

우선 cap deploy:check를 실행해 보자. 위에 말한 releases, shared 디렉토리가 미리 만들어져 있어야 하고 git, rsync 등 필요한 명령이 실행 가능한지도 검사해 준다. 아마 필요한 디렉토리가 없다고 알려줄 거다.

cap deploy:setup 명령을 실행하면 필요한 디렉토리들을 만들어 준다. 다시 cap deploy:check로 검사해보고 문제가 없으면 OK.

WordPress의 wp-content/uploads

앞에 요약한 배포과정을 보면 알겠지만, 카피스트라노는 배포를 할 때마다 저장소의 소스코드를 가져오기 때문에 버전관리에 포함되지 않는 파일들은 shared 아래에 두고 심볼릭 링크하도록 하고 있다. 버전관리되지 않는 파일이란 사용자가 업로드하는 파일이나 로그파일, 캐시 등 애플리케이션이 생성하는 파일 등이 될 수 있겠다. 워드프레스에서는 wp-content/uploads 디렉토리가 여기에 해당하므로 이것을 미리 shared/uploads로 복사하거나 옮겨두자.

첫 번째 배포

자 이제 cap deploy를 실행하고 기다려 보자. 문제없이 실행됐다면 서버에서 아래와 비슷한 결과를 볼 수 있다.

[/home/foo/sites/bar]$ ls -l
total 12
lrwxrwxrwx 1 foo foo 62 Aug 30 22:57 current -> /home/foo/sites/bar/releases/20120830145102
drwxrwxr-x 9 foo foo 4096 Aug 30 22:57 releases
drwxrwxr-x 4 foo foo 4096 Aug 28 20:44 shared

웹서버 설정

서버에서는 바꿀게 별로 없다. 아마도 document root가 바뀌었을테니 설정파일에 반영한 다음 재시작하자. 나는 CentOS에서 nginx에 fast-cgi를 쓰고 있어서 아래처럼 했다.

$ sudo vim /opt/nginx/conf/nginx.conf
server { .... root ....; } <-- 여기 수정함
$ sudo /etc/init.d/nginx restart
$ sudo /etc/init.d/php_cgi restart

References

http://theme.fm/2011/09/deploying-wordpress-with-capistrano-part-2-staging-servers-tagging-database-security-2213/ https://github.com/capistrano/capistrano/blob/master/lib/capistrano/recipes/deploy.rb

About Sanghyun Park

a.k.a Baxang. Software engineer lives in Sydney, Australia born in Seoul, South Korea.