서버로 배포하기

우선 서버 셋팅이 완료된 상태로 가정하고 Capistrano 3를 이용하여 배포하는 과정을 진행해 보기로 한다.

서버 셋팅

배포할 서버에는 아래와 같은 프로그램을 미리 설치해 둔다. 여기서는 테스트 목적으로 배포할 것이기 때문에, 가상서버를 준비하기로 한다. 가상머신용 툴로는 VMware Fusion v6.0.4를 사용하였다. 무료로 사용할 수 있는 툴로는 오라클사의 VM VirtualBox가 있으며 여기를 방문하면 패키지를 다운로드 받아 설치할 수 있다. 우분투 12.04 서버 셋팅하기를 참고하면 VirtualBox로 가상 서버를 생성하여 서버 환경을 구축할 수 있다.

가상머신에 우분투 12.04 버전을 설치하고 아래의 프로그램을 설치한다. (구글 검색하면 우분투 서버 설치에 대한 블로그들이 많이 있다.)

  • git : 소스관리
  • Nginx : 웹서버
  • MySQL : 데이터베이스 서버
  • Nodejs : 서버사이드 자바스크립트

Caution MySQL DB 서버의 경우 원격서버의 /etc/mysql/conf.d/ 디렉토리에 deployer.cnf 파일을 새로 추가하고 아래와 같이 문자 인코딩을 uft8로 추가해 주어야 한글 인코딩 문제를 해결할 수 있다.

root@ubuntu $ sudo vi /etc/mysql/conf.d/deployer.cnf
[mysqld]
character-set-server = utf8

그리고 배포 전용 계정('deployer')도 추가해 둔다.

Info 아직 서버에 배포용 계정을 생성하지 않았다면 서버로 접속한 후 아래와 같이 계정을 생성한다.

root@ubuntu $ sudo adduser deployer
root@ubutnu $ sudo addgroup admin
root@ubuntu $ sudo usermod -a -G admin deployer

서버로 접속해서 nginx.conf 파일의 user 속성을 변경한다.

root@ubuntu $ sudo vi /etc/nginx/nginx.conf
user deployer;

배포시에 sudo 실행시 deployer 계정에 대한 비밀번호를 묻게 되는데 이를 방지 하기 위해서, 서버로 접속한 후에 아래와 같이 명령을 실행하고,

root@ubuntu $ sudo visudo

맨 아래 줄에 아래와 같이 추가해 준다.

deployer ALL=(ALL) NOPASSWD: ALL

또한, 매번 ssh로 서버에 접속할 때마다 비밀번호를 입력하는 절차를 생략하기 위해서, 아래와 같이 ssh 키를 서버로 복사한다.

$ ssh-copy-id -i ~/.ssh/id_rsa deployer@ubuntu.vm
deployer@ubuntu.vm's password:

Info 해당 디렉토리에 id_rsa 파일이 없다면 SSH Key 생성하기를 참고하여 생성한다.

이후부터는 ssh로 연결시에 비밀번호 입력없이 바로 접속이 된다.

Note 여기서 사용한 ubuntu.vm은 가상머신으로 설치한 우분투 서버의 가상 도메인이다. 도메인 대신 서버의 ip를 지정해도 된다. 맥 사용자들은 Horst라는 툴을 사용하면 /etc/hosts 파일을 GUI로 편하게 관리할 수 있다.

제대로 설정이 되었다면 아래의 명령을 실행했을 때 비밀번호 입력없이 결과를 확인할 수 있다.

$ ssh deployer@ubuntu.vm 'hostname; uptime'
ubuntu
 10:26:54 up  3:49,  1 user,  load average: 0.02, 0.02, 0.05

Caution 복수개의 서버를 설치 중이라면 모든 서버에서 위의 작업을 해 주어야 한다.

database.yml 설정

운영 데이터베이스 서버의 셋팅을 변경하기 위해서, config/database.yml 파일을 열고 아래와 같이 수정한다.

default: &default
  adapter: sqlite3
  pool: 5
  timeout: 5000

development:
  <<: *default
  database: db/development.sqlite3

test:
  <<: *default
  database: db/test.sqlite3

production:
  adapter: mysql2
  encoding: utf8
  pool: 5
  database: rcafe_production
  username: deployer
  password: <%= ENV["RCAFE_DATABASE_PASSWORD"] %>
  host: localhost

Gemfile의 추가

프로젝트의 Gemfile을 열고 파일 하단에 코멘트 처리되어 있는 두개의 젬을 활성화하고, Capistrano 관련 젬을 추가한 후 데이터베이스 젬을 환경에 맞게 추가한다.

# Use unicorn as the app server
gem 'unicorn'

# Use Capistrano for deployment
gem 'capistrano-rails', '1.1.1', group: :development

# 추가할 젬
gem 'capistrano-rbenv', '2.0.2', group: :development
gem 'capistrano-rbenv-install', '1.0.0', group: :development
gem 'capistrano-unicorn-nginx', '2.0.0', group: :development
gem 'capistrano-rails-console'
gem 'capistrano-rails-collection'
gem 'capistrano-rails-tail-log'

group :production do
  gem 'rb-readline'
end

# 데이터베이스 젬 그룹변경 및 추가
gem 'sqlite3', group: :development
gem 'mysql2', group: :production

프로젝트에 적용하기 위해서 번들 인스톨한다.

$ bin/bundle install

Capistrano의 초기화

그리고 프로젝트에 사용하기 위해서 Capistrano를 초기화한다.

$ cap install
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
Capified

Capistrano의 배포 환경설정

위에서 생성된 config/deploy.rb 파일을 열고 아래와 같이 변경한다.

set :application, 'rcafe'
set :repo_url, 'git@github.com:rorlab/rcafe.git'
set :deploy_to, '/home/deployer/apps/rcafe'

# rbenv 환경설정
set :rbenv_type, :user # or :system, depends on your rbenv setup
set :rbenv_ruby, '2.1.2'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w{rake gem bundle ruby rails}
set :rbenv_roles, :all # default values
set :rails_env, "production"

운영서버 배포 환경

다음 config/deploy/production.rb 파일을 열고 아래와 같이 변경한다.

role :app, %w{deployer@ubuntu.vm}
role :web, %w{deployer@ubuntu.vm}
role :db,  %w{deployer@ubuntu.vm}

set :nginx_server_name, 'ubuntu.vm'
set :unicorn_workers, 4

server 'ubuntu.vm', user: 'deployer', roles: %w{web app}

최적의 :unicorn_workers 수를 정하기 위해서 참고할 만한 글을 여기를 참고하기 바란다. 여기서는 4로 지정했다. 각자의 서버 환경에 따라 적절하게 조절할 필요가 있다.

secrets.yml 파일의 옵션 변경

config/secrets.yml 파일을 열고 아래와 같이 변경한다.

production:
  secret_key_base: <%= ENV["RCAFE_SECRET_KEY_BASE"] %>

Capfile 설정

위에서 RCAFE_를 환경변수 이름 앞에 추가한다.

Capfile을 열고 아래와 같이 변경한다.

# Load DSL and Setup Up Stages
require 'capistrano/setup'

# Includes default deployment tasks
require 'capistrano/deploy'

# Includes tasks from other gems included in your Gemfile
require 'capistrano/rbenv'
require 'capistrano/rbenv_install'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/rails/console'
require 'capistrano/rails/collection'
require 'capistrano/unicorn_nginx'
require 'capistrano/rails_tail_log'

# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

서버 시스템에 환경변수 지정

서버에 접속한 후 /etc/environment 파일을 열고 아래와 같이 추가한다.

RCAFE_SECRET_KEY_BASE='xxxxxxxxxxxxxx'
RCAFE_DATABASE_PASSWORD='xxxxxxx'

Note 로컬 프로젝트 디렉토리에서 아래와 같이 명령을 실행하면 secret 키를 생성할 수 있다

$ bin/rake secret

이 때 생성된 키를 위의 RCAFE_SECRET_KEY_BASE 값으로 할당하면 된다.

데이터베이스의 생성

서버에 접속하여 아래와 같이 데이터베이스를 생성하고 권한을 부여한다.

deployer@ubuntu $ mysql -u root -p
mysql> create database rcafe_production;
mysql> grant usage on *.* to deployer@localhost identified by 'password';
mysql> grant all privileges on rcafe_production.* to deployer@localhost;

운영서버의 셋업

배포 전에 아래와 같은 명령으로 서버의 셋팅 작업을 한다.

$ cap production setup

아래와 같이 루비 2.2.0 설치 중 에러가 발생하면,

INFO [b3b4b5bb] Running /usr/bin/env ~/.rbenv/bin/rbenv install 2.2.0 as deployer@ubuntu14.vm
DEBUG [b3b4b5bb] Command: /usr/bin/env ~/.rbenv/bin/rbenv install 2.2.0
DEBUG [b3b4b5bb] 	Downloading ruby-2.2.0.tar.gz...
DEBUG [b3b4b5bb] 	-> http://dqw8nmjcqpjn7.cloudfront.net/7671e394abfb5d262fbcd3b27a71bf78737c7e9347fa21c39e58b0bb9c4840fc
DEBUG [b3b4b5bb] 	Installing ruby-2.2.0...
DEBUG [b3b4b5bb]
DEBUG [b3b4b5bb] 	BUILD FAILED
DEBUG [b3b4b5bb] 	 (Ubuntu 14.10 using ruby-build 20150130)
DEBUG [b3b4b5bb]
DEBUG [b3b4b5bb] 	Inspect or clean up the working tree at /tmp/ruby-build.20150202122321.28969
DEBUG [b3b4b5bb] 	Results logged to /tmp/ruby-build.20150202122321.28969.log
DEBUG [b3b4b5bb]
DEBUG [b3b4b5bb] 	Last 10 log lines:
DEBUG [b3b4b5bb] 	./libffi-3.2.1/.libs/libffi.a: error adding symbols: Bad value
DEBUG [b3b4b5bb] 	collect2: error: ld returned 1 exit status
DEBUG [b3b4b5bb] 	Makefile:325: recipe for target '../../.ext/x86_64-linux/fiddle.so' failed
DEBUG [b3b4b5bb] 	make[2]: *** [../../.ext/x86_64-linux/fiddle.so] Error 1
DEBUG [b3b4b5bb] 	make[2]: Leaving directory '/tmp/ruby-build.20150202122321.28969/ruby-2.2.0/ext/fiddle'
DEBUG [b3b4b5bb] 	exts.mk:177: recipe for target 'ext/fiddle/all' failed
DEBUG [b3b4b5bb] 	make[1]: *** [ext/fiddle/all] Error 2
DEBUG [b3b4b5bb] 	make[1]: Leaving directory '/tmp/ruby-build.20150202122321.28969/ruby-2.2.0'
DEBUG [b3b4b5bb] 	uncommon.mk:187: recipe for target 'build-ext' failed
DEBUG [b3b4b5bb] 	make: *** [build-ext] Error 2

서버에 접속하여 아래와 같이 libffi-dev 라이브러리를 설치하고 다시 시도한다.

deployer@ubuntu $ sudo apt-get libffi-dev

배포하기

이제 실제 배포 명령을 실행한다.

$ cap production deploy

버그 : 이 때 아래와 같은 에러가 발생하고 중단할 경우에는 배포서버로 접속한 후 '...' 내용을 복사해서 실행하고 한번더 deploy하면 해결된다. 아직 이런 현상의 이유를 알 수 없다.

  Couldn't reload, starting 'cd /home/deployer/apps/blog/current && ( RBENV_ROOT=~/.rbenv RBENV_VERSION=2.1.2 RBENV_ROOT=~/.rbenv RBENV_VERSION=2.1.2 ~/.rbenv/bin/rbenv exec bundle exec unicorn -D -c /home/deployer/apps/blog/shared/config/unicorn.rb -E production )' instead

마지막으로 한가지 추가할 것은 서버에서 rake db:seed가 실행되도록 하여 기본 게시판을 생성하도록 한다.

$ cap production rails:rake:db:seed

에러 없이 배포가 완료되면 브라우저에서 확인한다.

소스코드의 관리

  • 이후 소스변경이 필요한 경우 커밋 후 git push한 다음, cap production deploy 명령을 실행하면 된다.
  • 잘 못 배포된 경우에는 cap production deploy:rollback 명령으로 취소할 수 있다.

지금까지 설명한 배포과정이 다소 길고 복잡한 감이 있다. 또한 각자의 환경에 따라 예상치 못했던 여러가지 에러가 발생할 것으로 생각한다. 각자 따라해 보고 문제가 발생하면 아래에 코멘트를 달아 함께 공유하고 고민해 보자.


Git소스 https://github.com/rorlakr/rcafe/tree/chapter_05_16


References:

  1. SSH Key 생성하기
  2. Sudo without password on Ubuntu
  3. NginX 주요 설정 (nginx.conf)