on hosts do |host|
as 'www-data' do
puts capture(:whoami)
end
end
SSHKit.config.default_env = { path: '/usr/local/libexec/bin:$PATH' }
on hosts do |host|
puts capture(:env)
end
on hosts do |host|
within '/var/log' do
puts capture(:head, '-n5', 'messages')
end
end
on hosts do |host|
with rack_env: :test do
puts capture("env | grep RACK_ENV")
end
end
on hosts do |host|
f = '/some/file'
if test("[ -d #{f} ]")
execute :touch, f
else
info "#{f} already exists on #{host}!"
end
end
SSHKit.config.output_vervosity
의 현재 로그 레벨에 따라 debug()
, info()
, warn()
, error()
, fatal()
메소드를 사용할 수 있다.
on hosts do |host|
as 'www-data' do
within '/var/log' do
puts capture(:whoami)
puts capture(:pwd)
end
end
end
이것은 아래와 같은 결과를 보여 준다.
www-data
/var/log
주의사항 : 이 예제는 약간 오해의 소지가 있다. 즉, www-data
계정은 쉘이 정의되어 있지 않아서 이 계정으로 변경할 수 없다.
on hosts do |host|
upload! '/config/database.yml', '/opt/my_project/shared/database.yml'
end
주의사항 : upload!()
메소드는 within()
, as()
등의 값을 받지 않는다. 이 문제는 라이브러리가 성숙해 짐에 따라 해결될 것이지만 현재로서는 아직 그대로다.
on hosts do |host|
file = File.open('/config/database.yml')
io = StringIO.new(....)
upload! file, '/opt/my_project/shared/database.yml'
upload! io, '/opt/my_project/shared/io.io.io'
end
IO 스트리밍하는 것은 "cat" 하기 보다는 뭔가를 업로드하는데 유용하다. 예를 들면,
on hosts do |host|
contents = StringIO.new('ALL ALL = (ALL) NOPASSWD: ALL')
upload! contents, '/etc/sudoers.d/yolo'
end
이것은 "echo(:cat, '...?...', '> /etc/sudoers.d/yolo')"와 같이 정확한 이스케이핑 순서를 기술해야 하는 수고를 덜어 준다.
주의사항 : upload!()
메소드는 within()
, as()
등의 값을 받지 않는다. 이 문제는 라이브러리가 성숙해 짐에 따라 해결될 것이지만 현재로서는 아직 그대로다.
on hosts do |host|
upload! '.', '/tmp/mypwd', recursive: true
end
이 경우에 recursive: true
옵션은 Net::{SCP, SFTP}
에서 사용가능한 옵션과 같은 것이다.
이 옵션들은 호스트마다 옵션을 달리 지정할 수 있다.
Netssh.configure do |ssh|
ssh.connection_timeout = 30
ssh.ssh_options = {
keys: %w(/home/user/.ssh/id_rsa),
forward_agent: false,
auth_methods: %w(publickey password)
}
end
on hosts do |host|
as user: 'www-data', group: 'project-group' do
within '/var/log' do
execute :touch, 'somefile'
execute :ls, '-l'
end
end
end
위의 설정에서, 생성된 파일은 www-data
계정과 project-group
그룹이 소유하게 된다.
umask
구성 옵션과 함께 사용하면, 로그인을 공유하지 않은 상태에서 팀 멤버간에 배포 스크립트를 쉽게 공유할 수 있다.
on hosts do
within "/var" do
puts capture(:pwd)
within :log do
puts capture(:pwd)
end
end
end
이것은 아래와 같이 출력된다.
/var/
/var/log
디렉토리 경로는 File.join()
를 이용해서 조합된다. 이 메소드는 경로 앞뒤에 오는 슬래시에 신경쓰지 않고도 각 부분을 정확하게 연결해서 조합해 준다. 이것은 File.join()
이 코드를 실행하는 머신에서 동작하기 때문에 오해의 여지가 있다. 만약 윈도우즈 운영환경에서 실행된다면 명령을 받는 머신에 따라 경로가 부정확하게 조합될 수 있다.
on hosts do
within '/opt/sites/example.com' do
background :rails, :server
end
end
이것은 nohup /usr/bin/env rails server > /dev/null &
와 같이 실행하는데, 레일스 프로세스를 백그라운드에서 실행하도록 하여 파일 시스템을 nohup 로그 파일로 더럽히지 않게 된다.
[역주] nohup
- 리눅스, 유닉스에서 쉘스크립트파일(*.sh)을 데몬형태로 실행시키는 프로그램
주의사항 : background()
메소드에 sleep 5
문자열을 넘겨 주면 제대로 동작하지 않을 것이다. 이것은 명령을 처리하는 규칙에 따른 것이어서, background(:sleep, "5")
와 같이(즉, sleep이 명령이고, 5는 인수) 호출해야 한다.
더 주의해야 할 사항 : background() task가 어떤 상황에서 특정 명령을 nohup ... &
로 감싸면, SSH 세션이 종료되어도 프로그램이 멈출 것이다.
on hosts do
# |host| 블록 인수는 옵션이다.
# 블록 인수로 넘기지 않으면 블록내에서는 nil 값을 가지게 된다.
end
/dev/null
로 리디렉트하기SSHKit.config.output = File.open('/dev/null')
class MyFormatter < SSHKit::Formatter::Abstract
def write(obj)
case obj.is_a? SSHKit::Command
# Do something here, see the SSHKit::Command documentation
end
end
end
SSHKit.config.output = MyFormatter.new($stdout)
SSHKit.config.output = MyFormatter.new(SSHKit.config.output)
SSHKit.config.output = MyFormatter.new(File.open('log/deploy.log', 'wb'))
host = SSHKit::Host.new('[email protected]')
host.password = "hackme"
on host do |host|
puts capture(:echo, "I don't care about security!")
end
on hosts do |host|
execute!(:echo, '"Example Message!" 1>&2; false')
end
이것은 #message
"Example Message!" 와 함께 'SSHKit::Command:Failed` 예외를 발생할 것이고 명령은 취소될 것이다.
on hosts do |host|
if test "[ -d /opt/sites ]"
within "/opt/sites" do
execute :git, :pull
end
else
execute :git, :clone, 'some-repository', '/opt/sites'
end
end
test()
명령는 execute()
와 동일하게 동작하지만 0가 아닌 값으로 명령이 종료할 때 false 값을 반환한다(man 1 test
가 하듯이). 논리값을 반환하기 때문에 블록내에서 제어 흐름의 방향을 지정할 때 사용될 수 있다.
host1 = SSHKit::Host.new '[email protected]'
host2 = SSHKit::Host.new '[email protected]'
on hosts do |host|
target = "/var/www/sites/"
if host.hostname =~ /org/
target += "dotorg"
else
target += "dotcom"
end
execute! :git, :clone, "git@git.#{host.hostname}", target
end
on 'example.com' do |host|
execute :uptime
end
이것은 example.com
호스트명을 SSHKit::Host
객체로 변환해서 해당 호스트에 대한 정확한 구성 상태를 가져온다.
호출하려는 명령이 스페이스 문자를 포함할 경우 맵핑되지 않을 것이다.
Command.new(:git, :push, :origin, :master).to_s
# => /usr/bin/env git push origin master
# (also: execute(:git, :push, :origin, :master)
Command.new("git push origin master").to_s
# => git push origin master
# (also: execute("git push origin master"))
이것은 ( if
와 test
와 같은) 쉘 내장 명령을 접근할 때 사용할 수 있다.
위의 기능을 확장한 것으로 아래와 같이 명령을 작성할 수 있다.
c = Command.new <<-EOCOMMAND
if test -d /var/log
then echo "Directory Exists"
fi
EOCOMMAND
c.to_s
# => if test -d /var/log; then echo "Directory Exists; fi
# (also: execute <<- EOCOMMAND........))
주의사항 : 스크립트를 한줄로 표시하는 로직은 세련되어 보이지 않지만 모든 테스트에서 제대로 동작한다. 핵심 사항은 if
가 /usr/bin/env if
로 매핑되지 않는다는 것인데, 이것은 문법상의 오류를 유발하지 않는다.
Rakefile
에 아래와 같은 것을 둘 수 있다.
require 'sshkit/dsl'
SSHKit.config.command_map[:rake] = "./bin/rake"
desc "Deploy the site, pulls from Git, migrate the db and precompile assets, then restart Passenger."
task :deploy do
on "example.com" do |host|
within "/opt/sites/example.com" do
execute :git, :pull
execute :bundle, :install, '--deployment'
execute :rake, 'db:migrate'
execute :rake, 'assets:precompile'
execute :touch, 'tmp/restart.txt'
end
end
end
Coordinator 가 모든 호스트를 Host 객체로 변환해 줄 것이다.
Coordinator.new("one.example.com", SSHKit::Host.new('two.example.com')).each in: :sequence do
puts capture :uptime
end
./lib/sshkit/dsl.rb
파일을 찾고할 수 있는 데, 여기에서 위와 거의 똑같은 코드를 볼 수 있지만 on()
메소드에 대해서 구현한 것이다.
v0.0.6
부터 구현됨
servers = %w{one.example.com two.example.com
three.example.com four.example.com}.collect do |s|
h = SSHKit::Host.new(s)
if s.match /(one|two)/
h.properties.roles = [:web]
else
h.properties.roles = [:app]
end
end
on servers do |host|
if host.properties.roles.include?(:web)
# 웹서버에 관련된 작업을 함.
elsif host.properties.roles.include?(:app)
# 어플리케이션 서버에 관련된 작업을 함.
end
end
SSHKit::Host#properties
는 OpenStruct
이며 어쨌던 유효성 검증이 되지 않기 때문에 이에 관해서는 개발자에게 달려 있거나 라이브러리가 이 메카니즘에 의미나 규칙을 추가해 주어야 한다.
on
을 run_locally
로 대체한다.
run_locally do
within '/tmp' do
execute :whoami
end
end