deployinator 0.0.7 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/deployinator.rb +2 -0
- data/lib/deployinator/check.rb +133 -0
- data/lib/deployinator/config.rb +8 -59
- data/lib/deployinator/deploy.rb +88 -268
- data/lib/deployinator/examples/bluepill.rb.erb +23 -0
- data/lib/deployinator/examples/config/deploy.rb +63 -31
- data/lib/deployinator/examples/config/deploy/staging.rb +18 -47
- data/lib/deployinator/examples/{deployer_authorized_keys.erb → deployment_authorized_keys.erb} +0 -0
- data/lib/deployinator/examples/{application_unicorn.rb.erb → unicorn.rb.erb} +6 -6
- data/lib/deployinator/helpers.rb +194 -0
- metadata +7 -7
- data/lib/deployinator/examples/application_bluepill.rb.erb +0 -27
- data/lib/deployinator/examples/config/deploy/staging_deployinator.rb +0 -19
- data/lib/deployinator/examples/config/deploy_deployinator.rb +0 -50
data/lib/deployinator.rb
CHANGED
@@ -0,0 +1,133 @@
|
|
1
|
+
namespace :deploy do
|
2
|
+
namespace :check do
|
3
|
+
|
4
|
+
task :bundle_command_map => ['deployinator:deployment_user'] do
|
5
|
+
set :bundle_binstubs, -> { shared_path.join('bundle', 'bin') }
|
6
|
+
set :bundle_gemfile, -> { release_path.join('Gemfile') }
|
7
|
+
SSHKit.config.command_map[:bundle] = [
|
8
|
+
"/usr/bin/env docker run --rm --tty",
|
9
|
+
"--user", fetch(:deployment_user_id),
|
10
|
+
"-e", "GEM_HOME=#{shared_path.join('bundle')}",
|
11
|
+
"-e", "GEM_PATH=#{shared_path.join('bundle')}",
|
12
|
+
"-e", "PATH=#{shared_path.join('bundle', 'bin')}:$PATH",
|
13
|
+
"--entrypoint", "#{shared_path.join('bundle', 'bin', 'bundle')}",
|
14
|
+
"--volume $SSH_AUTH_SOCK:/ssh-agent --env SSH_AUTH_SOCK=/ssh-agent",
|
15
|
+
"--volume #{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
16
|
+
"--volume /etc/passwd:/etc/passwd:ro",
|
17
|
+
"--volume /etc/group:/etc/group:ro",
|
18
|
+
"--volume /home:/home:rw",
|
19
|
+
fetch(:ruby_image_name)
|
20
|
+
].join(' ')
|
21
|
+
end
|
22
|
+
before 'bundler:install', 'deploy:check:bundle_command_map'
|
23
|
+
|
24
|
+
# Ensure Capistrano's inner rm commands always run using sudo
|
25
|
+
before 'deploy:started', :rm_command_map do
|
26
|
+
SSHKit.config.command_map[:rm] = "/usr/bin/env sudo rm"
|
27
|
+
end
|
28
|
+
|
29
|
+
if Rake::Task.task_defined?("deploy:cleanup")
|
30
|
+
# Append dependancy to existing cleanup task
|
31
|
+
task 'deploy:cleanup' => 'deploy:check:rm_command_map'
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'Ensure all deployinator specific settings are set, and warn and raise if not.'
|
35
|
+
task :settings do
|
36
|
+
{
|
37
|
+
(File.dirname(__FILE__) + "/examples/config/deploy.rb") => 'config/deploy.rb',
|
38
|
+
(File.dirname(__FILE__) + "/examples/config/deploy/staging.rb") => "config/deploy/#{fetch(:stage)}.rb"
|
39
|
+
}.each do |abs, rel|
|
40
|
+
Rake::Task['deployinator:settings'].invoke(abs, rel)
|
41
|
+
Rake::Task['deployinator:settings'].reenable
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# TODO make this better
|
46
|
+
before 'deploy:check', :templates do
|
47
|
+
run_locally do
|
48
|
+
keys_template = File.expand_path("./templates/deploy/deployment_authorized_keys.erb")
|
49
|
+
bluepill_template = File.expand_path("./templates/deploy/bluepill.rb.erb")
|
50
|
+
unicorn_template = File.expand_path("./templates/deploy/unicorn.rb.erb")
|
51
|
+
{
|
52
|
+
"You need a templates/deploy/deployment_authorized_keys.erb file." => keys_template,
|
53
|
+
"You need a templates/deploy/bluepill.rb.erb file." => bluepill_template,
|
54
|
+
"You need a templates/deploy/unicorn.rb.erb file." => unicorn_template,
|
55
|
+
}.each do |message, file|
|
56
|
+
fatal(message) and raise unless File.exists? file
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
before 'deploy:check', 'deployinator:deployment_user'
|
62
|
+
before 'deploy:check', 'deployinator:webserver_user'
|
63
|
+
|
64
|
+
task :root_dir_permissions => ['deployinator:deployment_user', 'deployinator:webserver_user'] do
|
65
|
+
on roles(:app) do
|
66
|
+
as :root do
|
67
|
+
[fetch(:deploy_to), Pathname.new(fetch(:deploy_to)).join("../")].each do |dir|
|
68
|
+
if directory_exists?(dir)
|
69
|
+
execute "chown", "#{fetch(:deployment_user_id)}:#{fetch(:webserver_user_id)}", dir
|
70
|
+
execute "chmod", "2750", dir
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
before 'deploy:check:directories', 'deploy:check:root_dir_permissions'
|
77
|
+
|
78
|
+
before 'deploy:check', :webserver_running do
|
79
|
+
on roles(:app) do
|
80
|
+
if container_exists?(fetch(:webserver_container_name))
|
81
|
+
unless container_is_running?(fetch(:webserver_container_name))
|
82
|
+
warn "The webserver container named #{fetch(:webserver_container_name)} exists but is not running. You can still deploy your code, but you need this, start it, or re-run setup with something like nginxinator."
|
83
|
+
ask :return_to_continue, nil
|
84
|
+
set :nothing, fetch(:return_to_continue)
|
85
|
+
end
|
86
|
+
else
|
87
|
+
warn "No webserver container named #{fetch(:webserver_container_name)} exists! You can still deploy your code, but you need this, set it up with something like nginxinator."
|
88
|
+
ask :return_to_continue, nil
|
89
|
+
set :nothing, fetch(:return_to_continue)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
before 'deploy:check', :postgres_running do
|
95
|
+
on primary fetch(:migration_role) do
|
96
|
+
if container_exists?(fetch(:postgres_container_name))
|
97
|
+
if container_is_running?(fetch(:postgres_container_name))
|
98
|
+
unless localhost_port_responding?(fetch(:postgres_port))
|
99
|
+
fatal "Port #{fetch(:postgres_port)} is not responding, we won't be able to db:migrate!"
|
100
|
+
raise
|
101
|
+
end
|
102
|
+
else
|
103
|
+
fatal "#{fetch(:postgres_container_name)} exists but is not running, we won't be able to db:migrate!"
|
104
|
+
raise
|
105
|
+
end
|
106
|
+
else
|
107
|
+
fatal "#{fetch(:postgres_container_name)} does not exist, we won't be able to db:migrate!"
|
108
|
+
raise
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
before 'deploy:check', :ensure_cadvisor do
|
114
|
+
on roles(:app), in: :sequence, wait: 5 do
|
115
|
+
if fetch(:use_cadvisor, true)
|
116
|
+
if container_exists?("cadvisor")
|
117
|
+
if container_is_running?("cadvisor")
|
118
|
+
info "cadvisor is already running."
|
119
|
+
else
|
120
|
+
info "Restarting existing cadvisor container."
|
121
|
+
execute "docker", "start", "cadvisor"
|
122
|
+
end
|
123
|
+
else
|
124
|
+
execute("docker", "run", fetch(:docker_run_cadvisor_command))
|
125
|
+
end
|
126
|
+
else
|
127
|
+
info "Not using cadvisor."
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
data/lib/deployinator/config.rb
CHANGED
@@ -1,69 +1,17 @@
|
|
1
1
|
namespace :deployinator do
|
2
2
|
|
3
|
-
# desc 'Ensure all deployinator specific settings are set, and warn and raise if not.'
|
4
|
-
# task :check_settings do
|
5
|
-
# run_locally do
|
6
|
-
# deploy_rb = File.expand_path("./config/deploy.rb")
|
7
|
-
# deploy_dir = File.expand_path("./config/deploy")
|
8
|
-
# {
|
9
|
-
# "Do not \"set :bundle_binstubs, ....\", deployinator will overwrite it and you'll be confused." =>
|
10
|
-
# system("grep -R -q -E '^set\ :bundle_binstubs' #{deploy_rb} #{deploy_dir}"),
|
11
|
-
# "Do not \"set :bundle_gemfile, ....\", deployinator will overwrite it and you'll be confused." =>
|
12
|
-
# system("grep -R -q -E '^set\ :bundle_gemfile' #{deploy_rb} #{deploy_dir}"),
|
13
|
-
# "You need \"set :domain, 'your.domain.com'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
|
14
|
-
# fetch(:domain).nil?,
|
15
|
-
# "You need \"set :nginx_container_name, 'your_nginx_container_name'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
|
16
|
-
# fetch(:nginx_container_name).nil?,
|
17
|
-
# "You need \"set :external_socket_path, 'your_external_socket_path'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
|
18
|
-
# fetch(:external_socket_path).nil?,
|
19
|
-
# "You need \"set :postgres_container_name, 'your_postgres_container_name'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
|
20
|
-
# fetch(:postgres_container_name).nil?,
|
21
|
-
# "You need \"set :postgres_port, 'your_postgres_port_number'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
|
22
|
-
# fetch(:postgres_port).nil?,
|
23
|
-
# "You need \"set :ruby_image_name, 'your_ruby_image_name'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
|
24
|
-
# fetch(:ruby_image_name).nil?,
|
25
|
-
# "You need \"set :ruby_container_name, 'your_ruby_container_name'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
|
26
|
-
# fetch(:ruby_container_name).nil?,
|
27
|
-
# "You need \"set :ruby_container_max_mem_mb, 'your_ruby_container_max_mem_mb'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
|
28
|
-
# fetch(:ruby_container_max_mem_mb).nil?
|
29
|
-
# }.each do |message, true_false|
|
30
|
-
# fatal(message) and raise if true_false
|
31
|
-
# end
|
32
|
-
# end
|
33
|
-
# end
|
34
|
-
# before 'deploy:starting', 'deployinator:check_settings'
|
35
|
-
|
36
|
-
# TODO make this better
|
37
|
-
task :check_templates do
|
38
|
-
run_locally do
|
39
|
-
keys_template = File.expand_path("./templates/deploy/deployer_authorized_keys.erb")
|
40
|
-
bluepill_template = File.expand_path("./templates/deploy/#{fetch(:application)}_bluepill.rb.erb")
|
41
|
-
unicorn_template = File.expand_path("./templates/deploy/#{fetch(:application)}_unicorn.rb.erb")
|
42
|
-
{
|
43
|
-
"You need a templates/deploy/deployer_authorized_keys.erb file." => keys_template,
|
44
|
-
"You need a templates/deploy/#{fetch(:application)}_bluepill.rb.erb file." => bluepill_template,
|
45
|
-
"You need a templates/deploy/#{fetch(:application)}_unicorn.rb.erb file." => unicorn_template,
|
46
|
-
}.each do |message, file|
|
47
|
-
fatal(message) and raise unless File.exists? file
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
before 'deploy:starting', 'deployinator:check_templates'
|
52
|
-
|
53
3
|
desc 'Write example config files'
|
54
4
|
task :write_example_configs do
|
55
5
|
run_locally do
|
56
6
|
execute "mkdir", "-p", "config/deploy", "templates/deploy"
|
57
7
|
{
|
58
|
-
"examples/Capfile"
|
59
|
-
"examples/config/deploy.rb"
|
60
|
-
"examples/config/
|
61
|
-
"examples/
|
62
|
-
"examples/
|
63
|
-
"examples/
|
64
|
-
"examples/
|
65
|
-
"examples/application_unicorn.rb.erb" => "templates/deploy/#{fetch(:application, "my_app")}_unicorn_example.rb.erb",
|
66
|
-
"examples/application_bluepill.rb.erb" => "templates/deploy/#{fetch(:application, "my_app")}_bluepill_example.rb.erb"
|
8
|
+
"examples/Capfile" => "Capfile_example",
|
9
|
+
"examples/config/deploy.rb" => "config/deploy_example.rb",
|
10
|
+
"examples/config/deploy/staging.rb" => "config/deploy/staging_example.rb",
|
11
|
+
"examples/Dockerfile" => "templates/deploy/Dockerfile_example",
|
12
|
+
"examples/deployment_authorized_keys.erb" => "templates/deploy/deployment_authorized_keys_example.erb",
|
13
|
+
"examples/unicorn.rb.erb" => "templates/deploy/unicorn_example.rb.erb",
|
14
|
+
"examples/bluepill.rb.erb" => "templates/deploy/bluepill_example.rb.erb"
|
67
15
|
}.each do |source, destination|
|
68
16
|
config = File.read(File.dirname(__FILE__) + "/#{source}")
|
69
17
|
File.open("./#{destination}", 'w') { |f| f.write(config) }
|
@@ -72,4 +20,5 @@ namespace :deployinator do
|
|
72
20
|
info "Now remove the '_example' portion of their names or diff with existing files and add the needed lines."
|
73
21
|
end
|
74
22
|
end
|
23
|
+
|
75
24
|
end
|
data/lib/deployinator/deploy.rb
CHANGED
@@ -3,67 +3,41 @@ lock '3.2.1'
|
|
3
3
|
|
4
4
|
namespace :deploy do
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
"-e", "PATH=#{fetch(:deploy_to)}/shared/bundle/bin:$PATH",
|
13
|
-
"--entrypoint", "#{fetch(:deploy_to)}/shared/bundle/bin/bundle",
|
14
|
-
"--volume $SSH_AUTH_SOCK:/ssh-agent --env SSH_AUTH_SOCK=/ssh-agent",
|
15
|
-
"--volume #{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
16
|
-
"--volume /etc/passwd:/etc/passwd:ro",
|
17
|
-
"--volume /etc/group:/etc/group:ro",
|
18
|
-
"--volume /home:/home:rw",
|
19
|
-
fetch(:ruby_image_name)
|
20
|
-
].join(' ')
|
21
|
-
end
|
22
|
-
before 'bundler:install', 'deploy:set_bundle_command_map'
|
23
|
-
|
24
|
-
task :set_rm_command_map do
|
25
|
-
SSHKit.config.command_map[:rm] = "/usr/bin/env sudo rm"
|
26
|
-
end
|
27
|
-
before 'deploy:started', 'deploy:set_rm_command_map'
|
28
|
-
|
29
|
-
# Append dependancy to existing cleanup task
|
30
|
-
task :cleanup => :set_rm_command_map
|
31
|
-
|
32
|
-
if Rake::Task.task_defined?("bundler:install")
|
33
|
-
#desc 'Copies .git folder'
|
34
|
-
task :copy_git do
|
35
|
-
unless ENV['from_local'] == "true"
|
36
|
-
on roles(:app) do
|
37
|
-
within release_path do
|
38
|
-
execute :cp, '-r', repo_path, '.git'
|
39
|
-
end
|
6
|
+
#desc 'Copies .git folder to support .gemspecs that run git commands'
|
7
|
+
task :copy_git do
|
8
|
+
unless ENV['from_local'] == "true"
|
9
|
+
on roles(:app) do
|
10
|
+
within release_path do
|
11
|
+
execute :cp, '-r', repo_path, '.git'
|
40
12
|
end
|
41
|
-
end
|
13
|
+
end
|
42
14
|
end
|
43
|
-
before 'bundler:install', 'deploy:copy_git'
|
44
15
|
end
|
16
|
+
before 'bundler:install', 'deploy:copy_git'
|
45
17
|
|
46
|
-
# If defined, overwrite :assets:precompile to use docker
|
47
18
|
if Rake::Task.task_defined?("deploy:assets:precompile")
|
19
|
+
# Overwrite :assets:precompile to use docker
|
48
20
|
Rake::Task["deploy:assets:precompile"].clear
|
49
21
|
namespace :assets do
|
50
|
-
task :precompile => :
|
22
|
+
task :precompile => ['deployinator:deployment_user'] do
|
51
23
|
on roles(fetch(:assets_roles)) do
|
52
24
|
execute(
|
53
|
-
"docker", "run", "--rm", "--tty", "--user", fetch(:
|
54
|
-
"-w",
|
25
|
+
"docker", "run", "--rm", "--tty", "--user", fetch(:deployment_user_id),
|
26
|
+
"-w", release_path,
|
55
27
|
"--link", "#{fetch(:postgres_container_name)}:postgres",
|
56
|
-
"--entrypoint", "
|
28
|
+
"--entrypoint", "/bin/bash",
|
57
29
|
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
58
|
-
fetch(:ruby_image_name), "
|
30
|
+
fetch(:ruby_image_name), "-c",
|
31
|
+
"\"umask", "0007", "&&", "#{shared_path.join('bundle', 'bin', 'rake')}",
|
32
|
+
"assets:precompile\""
|
59
33
|
)
|
60
34
|
end
|
61
35
|
end
|
62
36
|
end
|
63
37
|
end
|
64
38
|
|
65
|
-
# If defined, overwrite :cleanup_assets to use docker
|
66
39
|
if Rake::Task.task_defined?("deploy:cleanup_assets")
|
40
|
+
# Overwrite :cleanup_assets to use docker
|
67
41
|
Rake::Task["deploy:cleanup_assets"].clear
|
68
42
|
desc 'Cleanup expired assets'
|
69
43
|
task :cleanup_assets => [:set_rails_env] do
|
@@ -71,8 +45,8 @@ namespace :deploy do
|
|
71
45
|
execute(
|
72
46
|
"docker", "run", "--rm", "--tty",
|
73
47
|
"-e", "RAILS_ENV=#{fetch(:rails_env)}",
|
74
|
-
"-w",
|
75
|
-
"--entrypoint",
|
48
|
+
"-w", release_path,
|
49
|
+
"--entrypoint", shared_path.join('bundle', 'bin', 'bundle'),
|
76
50
|
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
77
51
|
fetch(:ruby_image_name), "exec", "rake", "assets:clean"
|
78
52
|
)
|
@@ -80,11 +54,11 @@ namespace :deploy do
|
|
80
54
|
end
|
81
55
|
end
|
82
56
|
|
83
|
-
# If defined, overwrite :migrate to use docker
|
84
57
|
if Rake::Task.task_defined?("deploy:migrate")
|
58
|
+
# Overwrite :migrate to use docker
|
85
59
|
Rake::Task["deploy:migrate"].clear
|
86
60
|
desc 'Runs rake db:migrate if migrations are set'
|
87
|
-
task :migrate => [:set_rails_env, :
|
61
|
+
task :migrate => [:set_rails_env, 'deploy:check:postgres_running'] do
|
88
62
|
on primary fetch(:migration_role) do
|
89
63
|
conditionally_migrate = fetch(:conditionally_migrate)
|
90
64
|
info '[deploy:migrate] Checking changes in /db/migrate' if conditionally_migrate
|
@@ -97,8 +71,8 @@ namespace :deploy do
|
|
97
71
|
"--link", "#{fetch(:postgres_container_name)}:postgres",
|
98
72
|
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
99
73
|
"-e", "RAILS_ENV=#{fetch(:rails_env)}",
|
100
|
-
"--entrypoint",
|
101
|
-
"-w",
|
74
|
+
"--entrypoint", shared_path.join('bundle', 'bin', 'rake'),
|
75
|
+
"-w", release_path,
|
102
76
|
fetch(:ruby_image_name), "db:migrate"
|
103
77
|
)
|
104
78
|
end
|
@@ -106,168 +80,47 @@ namespace :deploy do
|
|
106
80
|
end
|
107
81
|
end
|
108
82
|
|
109
|
-
|
110
|
-
task :ensure_running_postgres do
|
111
|
-
on primary fetch(:migration_role) do
|
112
|
-
unless capture("nc", "127.0.0.1", fetch(:postgres_port), "<", "/dev/null", ">", "/dev/null;", "echo", "$?").strip == "0"
|
113
|
-
fatal "Port #{fetch(:postgres_port)} is not responding, cannot db:migrate!"
|
114
|
-
raise
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
# task :ensure_running_postgres do
|
119
|
-
# on primary fetch(:migration_role) do
|
120
|
-
# if test "docker", "inspect", fetch(:postgres_container_name), "&>", "/dev/null"
|
121
|
-
# if (capture "docker", "inspect",
|
122
|
-
# "--format='{{.State.Running}}'",
|
123
|
-
# fetch(:postgres_container_name)).strip == "true"
|
124
|
-
# else
|
125
|
-
# execute "docker", "start", fetch(:postgres_container_name)
|
126
|
-
# end
|
127
|
-
# else
|
128
|
-
# set :run_pg_setup, ask("'true' or 'false', - #{fetch(:postgres_container_name)} is not running, would you like to run 'rake pg:setup'?", "false")
|
129
|
-
# if fetch(:run_pg_setup) == "true"
|
130
|
-
# #load Gem.bin_path('bundler', 'bundle')
|
131
|
-
# #import 'postgresinator'
|
132
|
-
# #load './infrastructure/Rakefile'
|
133
|
-
# Rake::Task['pg:setup'].invoke
|
134
|
-
# else
|
135
|
-
# raise "#{fetch(:postgres_container_name)} is not running, can't run db:migrate!"
|
136
|
-
# end
|
137
|
-
# end
|
138
|
-
# end
|
139
|
-
# end
|
140
|
-
before 'deploy:started', :ensure_running_postgres
|
141
|
-
|
142
|
-
before 'deploy:check', :setup_deployer_user do
|
143
|
-
on "#{ENV['USER']}@#{fetch(:domain)}" do
|
144
|
-
as :root do
|
145
|
-
unless test "id", fetch(:deploy_username)
|
146
|
-
execute "adduser", "--disabled-password", "--gecos", "\"\"", fetch(:deploy_username)
|
147
|
-
execute "usermod", "-a", "-G", "sudo", fetch(:deploy_username)
|
148
|
-
execute "usermod", "-a", "-G", "docker", fetch(:deploy_username)
|
149
|
-
end
|
150
|
-
execute "mkdir", "-p", "/home/#{fetch(:deploy_username)}/.ssh"
|
151
|
-
# not actually using ERB interpolation, no need for an instance variable.
|
152
|
-
template_path = File.expand_path("./templates/deploy/#{fetch(:deploy_username)}_authorized_keys.erb")
|
153
|
-
generated_config_file = ERB.new(File.new(template_path).read).result(binding)
|
154
|
-
# upload! does not yet honor "as" and similar scoping methods
|
155
|
-
upload! StringIO.new(generated_config_file), "/tmp/authorized_keys"
|
156
|
-
execute "mv", "-b", "/tmp/authorized_keys", "/home/#{fetch(:deploy_username)}/.ssh/authorized_keys"
|
157
|
-
execute "chown", "-R", "#{fetch(:deploy_username)}:#{fetch(:deploy_username)}", "/home/#{fetch(:deploy_username)}/.ssh"
|
158
|
-
execute "chmod", "700", "/home/#{fetch(:deploy_username)}/.ssh"
|
159
|
-
execute "chmod", "600", "/home/#{fetch(:deploy_username)}/.ssh/authorized_keys"
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
task :ensure_www_data_user do
|
165
|
-
on "#{ENV['USER']}@#{fetch(:domain)}" do
|
166
|
-
as :root do
|
167
|
-
unless test "id", "www-data"
|
168
|
-
execute "adduser", "--disabled-password", "--gecos", "\"\"", "www-data"
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
after 'deploy:started', :ensure_www_data_user
|
174
|
-
|
175
|
-
task :set_deployer_user_id do
|
176
|
-
on roles(:app), in: :sequence, wait: 5 do
|
177
|
-
set :deployer_user_id, capture("id", "-u", fetch(:deploy_username)).strip
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
task :set_www_data_user_id do
|
182
|
-
on roles(:app), in: :sequence, wait: 5 do
|
183
|
-
set :www_data_user_id, capture("id", "-u", "www-data").strip
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
task :chown_log_dir => :set_www_data_user_id do
|
188
|
-
on roles(:app), in: :sequence, wait: 5 do
|
189
|
-
as :root do
|
190
|
-
unless test "[", "-d", "#{fetch(:deploy_to)}/shared/log", "]"
|
191
|
-
execute("mkdir", "-p", "#{fetch(:deploy_to)}/shared/log")
|
192
|
-
end
|
193
|
-
execute "chown", "-R", "#{fetch(:www_data_user_id)}:#{fetch(:www_data_user_id)}", "#{fetch(:deploy_to)}/shared/log"
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
before 'deploy:check:linked_dirs', :chown_log_dir
|
198
|
-
|
199
|
-
task :setup_deploy_to_dir => :set_deployer_user_id do
|
83
|
+
task :install_bundler => ['deployinator:deployment_user'] do
|
200
84
|
on roles(:app), in: :sequence, wait: 5 do
|
201
|
-
|
202
|
-
|
203
|
-
fetch(:
|
204
|
-
"
|
205
|
-
"
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
after :setup_deployer_user, :setup_deploy_to_dir
|
216
|
-
|
217
|
-
task :install_bundler => :set_deployer_user_id do
|
218
|
-
on roles(:app), in: :sequence, wait: 5 do
|
219
|
-
as :root do
|
220
|
-
unless test "[", "-f", "#{fetch(:deploy_to)}/shared/bundle/bin/bundle", "]"
|
221
|
-
execute(
|
222
|
-
"docker", "run", "--rm", "--tty",
|
223
|
-
"-e", "GEM_HOME=#{fetch(:deploy_to)}/shared/bundle",
|
224
|
-
"-e", "GEM_PATH=#{fetch(:deploy_to)}/shared/bundle",
|
225
|
-
"-e", "PATH=#{fetch(:deploy_to)}/shared/bundle/bin:$PATH",
|
226
|
-
"--entrypoint", "/usr/local/bin/gem",
|
227
|
-
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
228
|
-
fetch(:ruby_image_name), "install",
|
229
|
-
"--install-dir", "#{fetch(:deploy_to)}/shared/bundle",
|
230
|
-
"--bindir", "#{fetch(:deploy_to)}/shared/bundle/bin",
|
231
|
-
"--no-ri", "--no-rdoc", "--quiet", "bundler", "-v'#{fetch(:bundler_version)}'"
|
232
|
-
)
|
233
|
-
end
|
234
|
-
execute "chown", "-R", "#{fetch(:deployer_user_id)}:#{fetch(:deployer_user_id)}", "#{fetch(:deploy_to)}/shared/bundle"
|
85
|
+
unless file_exists?(shared_path.join('bundle', 'bin', 'bundle'))
|
86
|
+
execute(
|
87
|
+
"docker", "run", "--rm", "--tty", "--user", fetch(:deployment_user_id),
|
88
|
+
"-e", "GEM_HOME=#{shared_path.join('bundle')}",
|
89
|
+
"-e", "GEM_PATH=#{shared_path.join('bundle')}",
|
90
|
+
"-e", "PATH=#{shared_path.join('bundle', 'bin')}:$PATH",
|
91
|
+
"--entrypoint", "/bin/bash",
|
92
|
+
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
93
|
+
fetch(:ruby_image_name), "-c",
|
94
|
+
"\"umask", "0007", "&&" "/usr/local/bin/gem", "install",
|
95
|
+
"--install-dir", "#{shared_path.join('bundle')}",
|
96
|
+
"--bindir", shared_path.join('bundle', 'bin'),
|
97
|
+
"--no-ri", "--no-rdoc", "--quiet", "bundler", "-v'#{fetch(:bundler_version)}'\""
|
98
|
+
)
|
235
99
|
end
|
236
100
|
end
|
237
101
|
end
|
238
102
|
before 'bundler:install', 'deploy:install_bundler'
|
239
103
|
|
240
104
|
desc 'Restart application using bluepill restart inside the docker container.'
|
241
|
-
task :restart => [:
|
105
|
+
task :restart => ['deployinator:webserver_user', :install_config_files] do
|
242
106
|
on roles(:app), in: :sequence, wait: 5 do
|
243
|
-
|
244
|
-
|
245
|
-
fetch(:external_socket_path),
|
246
|
-
"#{fetch(:deploy_to)}/shared/tmp",
|
247
|
-
"#{fetch(:deploy_to)}/shared/log/production.log"
|
248
|
-
].join(' ')
|
249
|
-
execute "chown", "-R", "#{fetch(:www_data_user_id)}:#{fetch(:www_data_user_id)}", paths
|
250
|
-
end
|
251
|
-
if test "docker", "inspect", fetch(:ruby_container_name), "&>", "/dev/null"
|
252
|
-
if (capture "docker", "inspect",
|
253
|
-
"--format='{{.State.Restarting}}'",
|
254
|
-
fetch(:ruby_container_name)).strip == "true"
|
107
|
+
if container_exists?(fetch(:ruby_container_name))
|
108
|
+
if container_is_restarting?(fetch(:ruby_container_name))
|
255
109
|
execute("docker", "stop", fetch(:ruby_container_name))
|
256
110
|
end
|
257
|
-
if (
|
258
|
-
"--format='{{.State.Running}}'",
|
259
|
-
fetch(:ruby_container_name)).strip == "true"
|
111
|
+
if container_is_running?(fetch(:ruby_container_name))
|
260
112
|
execute(
|
261
113
|
"docker", "exec", "--tty",
|
262
114
|
fetch(:ruby_container_name),
|
263
|
-
|
115
|
+
shared_path.join('bundle', 'bin', 'bluepill'),
|
264
116
|
fetch(:application), "restart"
|
265
117
|
)
|
118
|
+
else
|
119
|
+
execute("docker", "start", fetch(:ruby_container_name))
|
266
120
|
end
|
267
121
|
else
|
268
122
|
as :root do
|
269
|
-
execute("rm", "-f",
|
270
|
-
execute "chown", "-R", "#{fetch(:deployer_user_id)}:#{fetch(:deployer_user_id)}", "#{fetch(:deploy_to)}/shared/bundle"
|
123
|
+
execute("rm", "-f", fetch(:webserver_socket_path).join('unicorn.pid'))
|
271
124
|
end
|
272
125
|
execute("docker", "run", fetch(:docker_run_bluepill_command))
|
273
126
|
end
|
@@ -279,27 +132,28 @@ namespace :deploy do
|
|
279
132
|
namespace :restart do
|
280
133
|
task :force do
|
281
134
|
on roles(:app), in: :sequence, wait: 5 do
|
282
|
-
if (
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
begin
|
296
|
-
execute("docker", "rm", fetch(:ruby_container_name))
|
297
|
-
rescue
|
298
|
-
sleep 5
|
135
|
+
if container_exists?(fetch(:ruby_container_name))
|
136
|
+
if container_is_running?(fetch(:ruby_container_name))
|
137
|
+
execute(
|
138
|
+
"docker", "exec", "--tty",
|
139
|
+
fetch(:ruby_container_name),
|
140
|
+
shared_path.join('bundle', 'bin', 'bluepill'),
|
141
|
+
fetch(:application), "stop"
|
142
|
+
)
|
143
|
+
sleep 5
|
144
|
+
execute("docker", "stop", fetch(:ruby_container_name))
|
145
|
+
execute("docker", "wait", fetch(:ruby_container_name))
|
146
|
+
else
|
147
|
+
end
|
299
148
|
begin
|
300
149
|
execute("docker", "rm", fetch(:ruby_container_name))
|
301
150
|
rescue
|
302
|
-
|
151
|
+
sleep 5
|
152
|
+
begin
|
153
|
+
execute("docker", "rm", fetch(:ruby_container_name))
|
154
|
+
rescue
|
155
|
+
fatal "We were not able to remove the container for some reason. Try running 'cap <stage> deploy:restart:force' again."
|
156
|
+
end
|
303
157
|
end
|
304
158
|
end
|
305
159
|
Rake::Task['deploy:restart'].invoke
|
@@ -307,70 +161,36 @@ namespace :deploy do
|
|
307
161
|
end
|
308
162
|
end
|
309
163
|
|
310
|
-
after :
|
311
|
-
on
|
312
|
-
#
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
warn "The nginx container named #{fetch(:nginx_container_name)} exists but is not running. (You need this, start it, or re-run setup with something like nginxinator.)"
|
319
|
-
end
|
320
|
-
else
|
321
|
-
warn "No nginx container named #{fetch(:nginx_container_name)} exists! (You need this, set it up with something like nginxinator.)"
|
322
|
-
end
|
323
|
-
end
|
324
|
-
end
|
164
|
+
# after :restart, :clear_cache do
|
165
|
+
# on roles(:web), in: :groups, limit: 3, wait: 10 do
|
166
|
+
# # Here we can do anything such as:
|
167
|
+
# # within release_path do
|
168
|
+
# # execute :rake, 'cache:clear'
|
169
|
+
# # end
|
170
|
+
# end
|
171
|
+
# end
|
325
172
|
|
326
|
-
|
173
|
+
task :install_config_files => ['deployinator:deployment_user', 'deployinator:webserver_user'] do
|
327
174
|
on roles(:app), in: :sequence, wait: 5 do
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
execute("docker", "run", fetch(:docker_run_cadvisor_command))
|
175
|
+
set :bluepill_config, -> { "bluepill.rb" }
|
176
|
+
set :unicorn_config, -> { "unicorn.rb" }
|
177
|
+
set :socket_path, -> { fetch(:webserver_socket_path) }
|
178
|
+
[fetch(:bluepill_config), fetch(:unicorn_config)].each do |config_file|
|
179
|
+
template_path = File.expand_path("./templates/deploy/#{config_file}.erb")
|
180
|
+
generated_config_file = ERB.new(File.new(template_path).read).result(binding)
|
181
|
+
set :final_path, -> { release_path.join('config', config_file) }
|
182
|
+
upload! StringIO.new(generated_config_file), "/tmp/#{config_file}"
|
183
|
+
execute("mv", "/tmp/#{config_file}", fetch(:final_path))
|
184
|
+
as :root do
|
185
|
+
execute("chown", "#{fetch(:deployment_user_id)}:#{fetch(:webserver_user_id)}", fetch(:final_path))
|
340
186
|
end
|
341
187
|
end
|
342
188
|
end
|
343
189
|
end
|
344
190
|
|
345
|
-
after :
|
346
|
-
|
347
|
-
|
348
|
-
# within release_path do
|
349
|
-
# execute :rake, 'cache:clear'
|
350
|
-
# end
|
351
|
-
end
|
352
|
-
end
|
353
|
-
|
354
|
-
task :install_config_files => [:set_deployer_user_id] do
|
355
|
-
on roles(:app), in: :sequence, wait: 5 do
|
356
|
-
set :bluepill_config, -> { "#{fetch(:application)}_bluepill.rb" }
|
357
|
-
set :unicorn_config, -> { "#{fetch(:application)}_unicorn.rb" }
|
358
|
-
set :socket_path, -> { fetch(:internal_socket_path) }
|
359
|
-
as 'root' do
|
360
|
-
[fetch(:bluepill_config), fetch(:unicorn_config)].each do |config_file|
|
361
|
-
@deploy_to = fetch(:deploy_to) # needed for ERB
|
362
|
-
@internal_socket_path = fetch(:socket_path) # needed for ERB
|
363
|
-
@application = fetch(:application) # needed for ERB
|
364
|
-
template_path = File.expand_path("./templates/deploy/#{config_file}.erb")
|
365
|
-
current_path = Pathname.new("#{fetch(:deploy_to)}/current")
|
366
|
-
generated_config_file = ERB.new(File.new(template_path).read).result(binding)
|
367
|
-
set :final_path, -> { fetch(:release_path, current_path).join('config', config_file) }
|
368
|
-
upload! StringIO.new(generated_config_file), "/tmp/#{config_file}"
|
369
|
-
execute("mv", "/tmp/#{config_file}", fetch(:final_path))
|
370
|
-
execute("chown", "#{fetch(:deployer_user_id)}:#{fetch(:deployer_user_id)}", fetch(:final_path))
|
371
|
-
execute("chmod", "664", fetch(:final_path))
|
372
|
-
end
|
373
|
-
end
|
191
|
+
after 'deploy:finished', :success_message do
|
192
|
+
run_locally do
|
193
|
+
info "That was a successful deploy!"
|
374
194
|
end
|
375
195
|
end
|
376
196
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
def red_unicorn_exec
|
2
|
+
"<%= shared_path.join('bundle', 'bin', 'red_unicorn') %> --env production --pid-file <%= fetch(:webserver_socket_path) %>/unicorn.pid --unicorn-exec <%= shared_path.join('bundle', 'bin', 'unicorn_rails') %> --unicorn-config <%= current_path.join('config', 'unicorn.rb') %>"
|
3
|
+
end
|
4
|
+
|
5
|
+
Bluepill.application("<%= fetch(:application) %>", :log_file => "<%= shared_path.join('log', 'bluepill.log') %>", :foreground => true) do |app|
|
6
|
+
app.process("unicorn") do |process|
|
7
|
+
process.pid_file = "<%= fetch(:webserver_socket_path) %>/unicorn.pid"
|
8
|
+
process.working_dir = "<%= current_path %>"
|
9
|
+
process.environment = {"BUNDLE_GEMFILE" => "<%= current_path.join('Gemfile') %>"}
|
10
|
+
process.start_command = "#{red_unicorn_exec} start"
|
11
|
+
process.stop_command = "#{red_unicorn_exec} stop"
|
12
|
+
process.restart_command = "'#{red_unicorn_exec} status; if [ $? = 0 ]; then #{red_unicorn_exec} restart; else #{red_unicorn_exec} start; fi'"
|
13
|
+
process.uid = process.gid = '<%= fetch(:webserver_username) %>'
|
14
|
+
process.start_grace_time = 30.seconds
|
15
|
+
process.stop_grace_time = 30.seconds
|
16
|
+
process.restart_grace_time = 60.seconds
|
17
|
+
process.monitor_children do |child_process|
|
18
|
+
child_process.stop_command = "kill -QUIT {{PID}}"
|
19
|
+
child_process.checks :mem_usage, :every => 30.seconds, :below => 300.megabytes, :times => [3,4], :fires => :stop
|
20
|
+
child_process.checks :cpu_usage, :every => 30.seconds, :below => 50, :times => [3,4], :fires => :stop
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,8 +1,16 @@
|
|
1
1
|
# config valid only for Capistrano 3.1
|
2
2
|
lock '3.2.1'
|
3
3
|
|
4
|
-
set :application,
|
5
|
-
set :repo_url,
|
4
|
+
set :application, 'my_app_name'
|
5
|
+
set :repo_url, 'git@example.com:me/my_repo.git'
|
6
|
+
set :preexisting_ssh_user, ENV['USER']
|
7
|
+
set :deployment_username, "deployer" # user with SSH access and passwordless sudo rights
|
8
|
+
set :webserver_username, "www-data" # less trusted web server user with limited write permissions
|
9
|
+
|
10
|
+
set :webserver_writeable_dirs, [shared_path.join('run'), shared_path.join("tmp"), shared_path.join("log")]
|
11
|
+
set :webserver_executable_dirs, [shared_path.join("bundle", "bin")]
|
12
|
+
set :ignore_permissions_dirs, [shared_path.join("postgres")]
|
13
|
+
set :webserver_socket_path, shared_path.join('run')
|
6
14
|
|
7
15
|
# Default branch is :master
|
8
16
|
# Always use the master branch in production:
|
@@ -14,24 +22,14 @@ end
|
|
14
22
|
# Default deploy_to directory is /var/www/my_app
|
15
23
|
# set :deploy_to, '/var/www/my_app'
|
16
24
|
|
17
|
-
# Default value for :scm is :git
|
18
|
-
# set :scm, :git
|
19
|
-
|
20
|
-
# Default value for :format is :pretty
|
21
|
-
# set :format, :pretty
|
22
|
-
|
23
25
|
# Default value for :log_level is :debug
|
24
26
|
# set :log_level, :debug
|
25
27
|
|
26
|
-
# Default value for :pty is false
|
27
|
-
# set :pty, true
|
28
|
-
|
29
28
|
# Default value for :linked_files is []
|
30
29
|
# set :linked_files, %w{config/database.yml}
|
31
30
|
|
32
31
|
# Default value for linked_dirs is []
|
33
32
|
# set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
|
34
|
-
set :linked_dirs, %w{log tmp}
|
35
33
|
|
36
34
|
# Default value for default_env is {}
|
37
35
|
# set :default_env, { path: "/opt/ruby/bin:$PATH" }
|
@@ -39,28 +37,62 @@ set :linked_dirs, %w{log tmp}
|
|
39
37
|
# Default value for keep_releases is 5
|
40
38
|
# set :keep_releases, 5
|
41
39
|
|
42
|
-
namespace :deploy do
|
43
40
|
|
44
|
-
|
45
|
-
|
46
|
-
task :restart do
|
47
|
-
on roles(:app), in: :sequence, wait: 5 do
|
48
|
-
# Your restart mechanism here, for example:
|
49
|
-
# execute :touch, release_path.join('tmp/restart.txt')
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
after :publishing, :restart
|
41
|
+
#----------------------------------------------------
|
42
|
+
## The values below shouldn't need changed under the majority of circumstances.
|
54
43
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
44
|
+
# Use `cap <stage> deploy from_local=true` to deploy your
|
45
|
+
# locally changed code instead of the code in the git repo. You can also add --trace.
|
46
|
+
if ENV['from_local']
|
47
|
+
if fetch(:current_stage) == "production"
|
48
|
+
run_locally do
|
49
|
+
fatal "You are trying to deploy to production using from_local, this should pretty much never be done."
|
50
|
+
end
|
51
|
+
ask :yes_no, "Are you positive you want to continue?"
|
52
|
+
if fetch(:yes_no).chomp.downcase == "yes"
|
53
|
+
set :scm, :copy
|
54
|
+
else
|
55
|
+
exit
|
61
56
|
end
|
62
57
|
end
|
63
|
-
|
58
|
+
else
|
59
|
+
set :scm, :git
|
64
60
|
end
|
65
61
|
|
66
|
-
|
62
|
+
#set :bundle_roles, :all # this is default
|
63
|
+
#set :bundle_servers, -> { release_roles(fetch(:bundle_roles)) } # this is default
|
64
|
+
#set :bundle_binstubs, -> { shared_path.join('bin') } # this is default
|
65
|
+
#set :bundle_binstubs, -> { shared_path.join('bundle', 'bin') } # this will be overwritten by deployinator
|
66
|
+
#set :bundle_gemfile, -> { release_path.join('Gemfile') } # this will be overwritten by deployinator
|
67
|
+
#set :bundle_path, -> { shared_path.join('bundle') } # this is default
|
68
|
+
#set :bundle_without, %w{development test}.join(' ') # this is default
|
69
|
+
#set :bundle_flags, '--deployment --quiet' # this is default
|
70
|
+
#set :bundle_flags, '--deployment'
|
71
|
+
#set :bundle_env_variables, {} # this is default
|
72
|
+
|
73
|
+
set :docker_run_bluepill_command, -> { [
|
74
|
+
"--tty", "--detach",
|
75
|
+
"--name", fetch(:ruby_container_name),
|
76
|
+
"-e", "GEM_HOME=#{shared_path.join('bundle')}",
|
77
|
+
"-e", "GEM_PATH=#{shared_path.join('bundle')}",
|
78
|
+
"-e", "BUNDLE_GEMFILE=#{current_path.join('Gemfile')}",
|
79
|
+
"-e", "PATH=#{shared_path.join('bundle', 'bin')}:$PATH",
|
80
|
+
"--link", "#{fetch(:postgres_container_name)}:postgres",
|
81
|
+
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
82
|
+
"--entrypoint", shared_path.join('bundle', 'bin', 'bluepill'),
|
83
|
+
"--restart", "always", "--memory", "#{fetch(:ruby_container_max_mem_mb)}m",
|
84
|
+
fetch(:ruby_image_name), "load",
|
85
|
+
current_path.join('config', 'bluepill.rb')
|
86
|
+
] }
|
87
|
+
|
88
|
+
set :docker_run_cadvisor_command, -> { [
|
89
|
+
"--detach",
|
90
|
+
"--name", "cadvisor",
|
91
|
+
"--volume", "/:/rootfs:ro",
|
92
|
+
"--volume", "/var/run:/var/run:rw",
|
93
|
+
"--volume", "/sys:/sys:ro",
|
94
|
+
"--volume", "/var/lib/docker/:/var/lib/docker:ro",
|
95
|
+
"--publish", "127.0.0.1:8080:8080",
|
96
|
+
"--restart", "always",
|
97
|
+
"google/cadvisor:latest"
|
98
|
+
] }
|
@@ -1,50 +1,21 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# Supports bulk-adding hosts to roles, the primary server in each group
|
4
|
-
# is considered to be the first unless any hosts have the primary
|
5
|
-
# property set. Don't declare `role :all`, it's a meta role.
|
1
|
+
set :domain, "my-app-staging.example.com"
|
2
|
+
set :user_host, "#{fetch(:deployment_username)}@#{fetch(:domain)}"
|
6
3
|
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
role :app, fetch(:user_host)
|
5
|
+
role :web, fetch(:user_host)
|
6
|
+
role :db, fetch(:user_host)
|
10
7
|
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
set :rails_env, 'production'
|
9
|
+
set :migration_role, 'app' # Defaults to 'db'
|
10
|
+
#set :conditionally_migrate, true # Defaults to false. If true, it's skip migration if files in db/migrate not modified
|
11
|
+
set :assets_roles, [:app] # Defaults to [:web]
|
12
|
+
#set :assets_prefix, 'prepackaged-assets' # Defaults to 'assets' this should match config.assets.prefix in your rails config/application.rb
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# Custom SSH Options
|
25
|
-
# ==================
|
26
|
-
# You may pass any option but keep in mind that net/ssh understands a
|
27
|
-
# limited set of options, consult[net/ssh documentation](http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start).
|
28
|
-
#
|
29
|
-
# Global options
|
30
|
-
# --------------
|
31
|
-
# set :ssh_options, {
|
32
|
-
# keys: %w(/home/rlisowski/.ssh/id_rsa),
|
33
|
-
# forward_agent: false,
|
34
|
-
# auth_methods: %w(password)
|
35
|
-
# }
|
36
|
-
#
|
37
|
-
# And/or per server (overrides global)
|
38
|
-
# ------------------------------------
|
39
|
-
# server 'example.com',
|
40
|
-
# user: 'user_name',
|
41
|
-
# roles: %w{web app},
|
42
|
-
# ssh_options: {
|
43
|
-
# user: 'user_name', # overrides user setting above
|
44
|
-
# keys: %w(/home/user_name/.ssh/id_rsa),
|
45
|
-
# forward_agent: false,
|
46
|
-
# auth_methods: %w(publickey password)
|
47
|
-
# # password: 'please use keys'
|
48
|
-
# }
|
49
|
-
|
50
|
-
require './config/deploy/staging_deployinator.rb'
|
14
|
+
set :webserver_container_name, "my-app-staging.example.com-nginx-80-443"
|
15
|
+
set :postgres_container_name, "my-app-staging.example.com-postgres-5432-master"
|
16
|
+
set :postgres_port, "5432"
|
17
|
+
set :ruby_image_name, "snarlysodboxer/ruby:1.9.3-p547"
|
18
|
+
set :ruby_container_name, "#{fetch(:domain)}-ruby-bluepill"
|
19
|
+
set :ruby_container_max_mem_mb, "1024"
|
20
|
+
set :bundler_version, "1.7.4"
|
21
|
+
set :use_cadvisor, true
|
data/lib/deployinator/examples/{deployer_authorized_keys.erb → deployment_authorized_keys.erb}
RENAMED
File without changes
|
@@ -1,12 +1,12 @@
|
|
1
|
-
current_path
|
2
|
-
shared_path
|
3
|
-
shared_bundler_gems_path
|
1
|
+
current_path = "<%= current_path %>"
|
2
|
+
shared_path = "<%= shared_path %>"
|
3
|
+
shared_bundler_gems_path = "<%= shared_path.join('bundle') %>"
|
4
4
|
|
5
5
|
worker_processes 4
|
6
6
|
working_directory current_path
|
7
|
-
listen "<%=
|
7
|
+
listen "<%= fetch(:webserver_socket_path) %>/unicorn.socket", :backlog => 64
|
8
8
|
timeout 300
|
9
|
-
pid "<%=
|
9
|
+
pid "<%= fetch(:webserver_socket_path) %>/unicorn.pid"
|
10
10
|
|
11
11
|
stderr_path "#{shared_path}/log/unicorn.stderr.log"
|
12
12
|
stdout_path "#{shared_path}/log/unicorn.stdout.log"
|
@@ -21,7 +21,7 @@ before_fork do |server, worker|
|
|
21
21
|
end
|
22
22
|
|
23
23
|
after_fork do |server, worker|
|
24
|
-
worker.user('
|
24
|
+
worker.user('<%= fetch(:webserver_username) %>', '<%= fetch(:webserver_username) %>') if Process.euid == 0
|
25
25
|
|
26
26
|
defined?(ActiveRecord::Base) and
|
27
27
|
ActiveRecord::Base.establish_connection
|
@@ -0,0 +1,194 @@
|
|
1
|
+
namespace :deployinator do
|
2
|
+
|
3
|
+
# These are the only two tasks using :preexisting_ssh_user
|
4
|
+
namespace :deployment_user do
|
5
|
+
#desc "Setup or re-setup the deployment user, idempotently"
|
6
|
+
task :setup do
|
7
|
+
on "#{fetch(:preexisting_ssh_user)}@#{fetch(:domain)}" do
|
8
|
+
as :root do
|
9
|
+
unix_user_add(fetch(:deployment_username)) unless unix_user_exists?(fetch(:deployment_username))
|
10
|
+
execute "usermod", "-a", "-G", "sudo,docker,#{fetch(:webserver_username)}", fetch(:deployment_username)
|
11
|
+
execute "mkdir", "-p", "/home/#{fetch(:deployment_username)}/.ssh"
|
12
|
+
template_path = File.expand_path("./templates/deploy/deployment_authorized_keys.erb")
|
13
|
+
generated_config_file = ERB.new(File.new(template_path).read).result(binding)
|
14
|
+
# upload! does not yet honor "as" and similar scoping methods
|
15
|
+
upload! StringIO.new(generated_config_file), "/tmp/authorized_keys"
|
16
|
+
execute "mv", "-b", "/tmp/authorized_keys", "/home/#{fetch(:deployment_username)}/.ssh/authorized_keys"
|
17
|
+
execute "chown", "-R", "#{fetch(:deployment_username)}:#{fetch(:deployment_username)}", "/home/#{fetch(:deployment_username)}/.ssh"
|
18
|
+
execute "chmod", "700", "/home/#{fetch(:deployment_username)}/.ssh"
|
19
|
+
execute "chmod", "600", "/home/#{fetch(:deployment_username)}/.ssh/authorized_keys"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
task :deployment_user do
|
26
|
+
on "#{fetch(:preexisting_ssh_user)}@#{fetch(:domain)}" do
|
27
|
+
as :root do
|
28
|
+
if unix_user_exists?(fetch(:deployment_username))
|
29
|
+
info "User #{fetch(:deployment_username)} already exists. You can safely re-setup the user with 'deployinator:deployment_user:setup'."
|
30
|
+
else
|
31
|
+
Rake::Task['deployinator:deployment_user:setup'].invoke
|
32
|
+
end
|
33
|
+
set :deployment_user_id, unix_user_get_id(fetch(:deployment_username))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
task :webserver_user do
|
39
|
+
on roles(:app) do
|
40
|
+
as :root do
|
41
|
+
unix_user_add(fetch(:webserver_username)) unless unix_user_exists?(fetch(:webserver_username))
|
42
|
+
set :webserver_user_id, unix_user_get_id(fetch(:webserver_username))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
task :sshkit_umask do
|
48
|
+
SSHKit.config.umask = "0027"
|
49
|
+
end
|
50
|
+
if Rake::Task.task_defined?('deploy:started')
|
51
|
+
before 'deploy:started', 'deployinator:sshkit_umask'
|
52
|
+
end
|
53
|
+
|
54
|
+
task :file_permissions => [:deployment_user, :webserver_user] do
|
55
|
+
on roles(:app) do
|
56
|
+
as :root do
|
57
|
+
ignore_options = fetch(:ignore_permissions_dirs).collect do |dir|
|
58
|
+
["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
|
59
|
+
end
|
60
|
+
|
61
|
+
# chown
|
62
|
+
execute "find", fetch(:deploy_to), ignore_options,
|
63
|
+
"-exec", "chown", "#{fetch(:deployment_user_id)}:#{fetch(:webserver_user_id)}", "{}", "+"
|
64
|
+
|
65
|
+
# chmod executable
|
66
|
+
fetch(:webserver_executable_dirs).each do |dir|
|
67
|
+
if directory_exists?(dir)
|
68
|
+
execute "find", dir, "-type", "f",
|
69
|
+
"-exec", "chmod", "0750", "{}", "+"
|
70
|
+
end
|
71
|
+
ignore_options += ["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
|
72
|
+
end
|
73
|
+
|
74
|
+
# chmod writable
|
75
|
+
fetch(:webserver_writeable_dirs).each do |dir|
|
76
|
+
if directory_exists?(dir)
|
77
|
+
execute "find", "-L", dir, "-type", "d",
|
78
|
+
"-exec", "chmod", "2770", "{}", "+"
|
79
|
+
execute "find", "-L", dir, "-type", "f",
|
80
|
+
"-exec", "chmod", "0660", "{}", "+"
|
81
|
+
end
|
82
|
+
ignore_options += ["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
|
83
|
+
end
|
84
|
+
|
85
|
+
# chmod
|
86
|
+
execute "find", fetch(:deploy_to), "-type", "d", ignore_options,
|
87
|
+
"-exec", "chmod", "2750", "{}", "+"
|
88
|
+
execute "find", fetch(:deploy_to), "-type", "f", ignore_options,
|
89
|
+
"-exec", "chmod", "0640", "{}", "+"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
after 'deploy:check', 'deployinator:file_permissions'
|
94
|
+
before 'deploy:restart', 'deployinator:file_permissions'
|
95
|
+
|
96
|
+
|
97
|
+
task :settings, [:absolute_path, :relative_path] do |t, args|
|
98
|
+
run_locally do
|
99
|
+
need_moar_settings = false
|
100
|
+
settings = File.read(args.absolute_path).split("\nset").collect do |line|
|
101
|
+
"set#{line}" if line =~ /^ :/
|
102
|
+
end.compact
|
103
|
+
if fetch(:print_all, false)
|
104
|
+
lines = "\nThe following settings are needed in your config (#{args.relative_path}).\n"
|
105
|
+
else
|
106
|
+
lines = "\nAdd the following setting(s) to your config (#{args.relative_path}) and try again:\n"
|
107
|
+
end
|
108
|
+
settings.each do |setting|
|
109
|
+
if fetch(setting.split(',')[0].split(':')[1].to_sym).nil? or fetch(:print_all, false)
|
110
|
+
lines += setting.chomp == setting ? "#{setting}\n" : setting
|
111
|
+
need_moar_settings = true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
if need_moar_settings
|
115
|
+
if fetch(:print_all, false)
|
116
|
+
info lines
|
117
|
+
else
|
118
|
+
fatal lines if lines.lines.count > 2
|
119
|
+
exit
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
def container_exists?(container_name)
|
128
|
+
test "bash", "-c", "\"docker", "inspect", container_name, "&>", "/dev/null\""
|
129
|
+
end
|
130
|
+
|
131
|
+
def container_is_running?(container_name)
|
132
|
+
test "[", "\"`docker", "inspect", "--format='{{.State.Running}}'",
|
133
|
+
"#{container_name} 2>&1`\"", "=", "\"true\"", "]"
|
134
|
+
end
|
135
|
+
|
136
|
+
def container_is_restarting?(container_name)
|
137
|
+
test "[", "\"`docker", "inspect", "--format='{{.State.Restarting}}'",
|
138
|
+
container_name, "2>&1`\"", "=", "\"true\"", "]"
|
139
|
+
end
|
140
|
+
|
141
|
+
def localhost_port_responding?(port)
|
142
|
+
test "nc", "127.0.0.1", port, "<", "/dev/null", ">", "/dev/null;",
|
143
|
+
"[", "`echo", "$?`", "-eq", "0", "]"
|
144
|
+
end
|
145
|
+
|
146
|
+
def unix_user_exists?(user)
|
147
|
+
test "bash", "-c", "\"id", user, "&>", "/dev/null\""
|
148
|
+
end
|
149
|
+
|
150
|
+
def unix_user_add(user)
|
151
|
+
execute "adduser", "--disabled-password", "--gecos", "\"\"", user
|
152
|
+
end
|
153
|
+
|
154
|
+
def unix_user_get_id(user)
|
155
|
+
capture("id", "-u", user).strip
|
156
|
+
end
|
157
|
+
|
158
|
+
def file_exists?(file)
|
159
|
+
test "[", "-f", file, "]"
|
160
|
+
end
|
161
|
+
|
162
|
+
def directory_exists?(dir)
|
163
|
+
test "[", "-d", dir, "]"
|
164
|
+
end
|
165
|
+
|
166
|
+
def check_stayed_running(name)
|
167
|
+
sleep 3
|
168
|
+
unless container_is_running?(name)
|
169
|
+
fatal "Container #{name} on #{fetch(:domain)} did not stay running more than 3 seconds"
|
170
|
+
exit
|
171
|
+
end
|
172
|
+
if container_is_restarting?(name)
|
173
|
+
fatal "Container #{name} on #{fetch(:domain)} is stuck restarting itself."
|
174
|
+
exit
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def create_container(name, command)
|
179
|
+
warn "Starting a new container named #{name} on #{fetch(:domain)}"
|
180
|
+
execute("docker", "run", command)
|
181
|
+
check_stayed_running(name)
|
182
|
+
end
|
183
|
+
|
184
|
+
def start_container(name)
|
185
|
+
warn "Starting an existing but non-running container named #{name}"
|
186
|
+
execute("docker", "start", name)
|
187
|
+
check_stayed_running(name)
|
188
|
+
end
|
189
|
+
|
190
|
+
def restart_container(name)
|
191
|
+
warn "Restarting a running container named #{name}"
|
192
|
+
execute("docker", "restart", name)
|
193
|
+
check_stayed_running(name)
|
194
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deployinator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-12-
|
12
|
+
date: 2014-12-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: capistrano
|
@@ -35,16 +35,16 @@ extra_rdoc_files: []
|
|
35
35
|
files:
|
36
36
|
- lib/deployinator.rb
|
37
37
|
- lib/deployinator/deploy.rb
|
38
|
+
- lib/deployinator/check.rb
|
38
39
|
- lib/deployinator/config.rb
|
40
|
+
- lib/deployinator/helpers.rb
|
39
41
|
- lib/deployinator/examples/Capfile
|
40
42
|
- lib/deployinator/examples/config/deploy.rb
|
41
|
-
- lib/deployinator/examples/config/deploy_deployinator.rb
|
42
43
|
- lib/deployinator/examples/config/deploy/staging.rb
|
43
|
-
- lib/deployinator/examples/config/deploy/staging_deployinator.rb
|
44
44
|
- lib/deployinator/examples/Dockerfile
|
45
|
-
- lib/deployinator/examples/
|
46
|
-
- lib/deployinator/examples/
|
47
|
-
- lib/deployinator/examples/
|
45
|
+
- lib/deployinator/examples/deployment_authorized_keys.erb
|
46
|
+
- lib/deployinator/examples/unicorn.rb.erb
|
47
|
+
- lib/deployinator/examples/bluepill.rb.erb
|
48
48
|
homepage: https://github.com/snarlysodboxer/deployinator
|
49
49
|
licenses:
|
50
50
|
- GNU
|
@@ -1,27 +0,0 @@
|
|
1
|
-
def red_unicorn_exec
|
2
|
-
"<%= @deploy_to %>/shared/bundle/bin/red_unicorn --env production --pid-file <%= @internal_socket_path %>/unicorn.pid --unicorn-exec <%= @deploy_to %>/shared/bundle/bin/unicorn_rails --unicorn-config <%= @deploy_to %>/current/config/<%= @application %>_unicorn.rb"
|
3
|
-
end
|
4
|
-
|
5
|
-
Bluepill.application("<%= @application %>", :log_file => "<%= @deploy_to %>/shared/log/bluepill.log", :foreground => true) do |app|
|
6
|
-
app.process("unicorn") do |process|
|
7
|
-
process.pid_file = "<%= @internal_socket_path %>/unicorn.pid"
|
8
|
-
process.working_dir = "<%= @deploy_to %>/current"
|
9
|
-
process.environment = {"BUNDLE_GEMFILE" => "<%= @deploy_to %>/current/Gemfile"}
|
10
|
-
process.start_command = "#{red_unicorn_exec} start"
|
11
|
-
process.stop_command = "#{red_unicorn_exec} stop"
|
12
|
-
process.restart_command = "'#{red_unicorn_exec} status; if [ $? = 0 ]; then #{red_unicorn_exec} restart; else #{red_unicorn_exec} start; fi'"
|
13
|
-
|
14
|
-
process.uid = process.gid = 'www-data'
|
15
|
-
|
16
|
-
process.start_grace_time = 30.seconds
|
17
|
-
process.stop_grace_time = 30.seconds
|
18
|
-
process.restart_grace_time = 60.seconds
|
19
|
-
|
20
|
-
process.monitor_children do |child_process|
|
21
|
-
child_process.stop_command = "kill -QUIT {{PID}}"
|
22
|
-
|
23
|
-
child_process.checks :mem_usage, :every => 30.seconds, :below => 300.megabytes, :times => [3,4], :fires => :stop
|
24
|
-
child_process.checks :cpu_usage, :every => 30.seconds, :below => 50, :times => [3,4], :fires => :stop
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
set :rails_env, 'production' # If the environment differs from the stage name
|
2
|
-
set :migration_role, 'app' # Defaults to 'db'
|
3
|
-
#set :conditionally_migrate, true # Defaults to false. If true, it's skip migration if files in db/migrate not modified
|
4
|
-
set :assets_roles, [:app] # Defaults to [:web]
|
5
|
-
#set :assets_prefix, 'prepackaged-assets' # Defaults to 'assets' this should match config.assets.prefix in your rails config/application.rb
|
6
|
-
|
7
|
-
# If you are using nginxinator and postgresinator, your settings would look similar to this:
|
8
|
-
# if using nginxinator, :nginx_container_name will be set by it, so leave this one commented out:
|
9
|
-
#set :nginx_container_name, "my-app-staging.example.com-nginx-80-443"
|
10
|
-
set :external_socket_path, "/my-app-staging.example.com-nginx-80-443-conf/run"
|
11
|
-
set :internal_socket_path, "/var/run/unicorn"
|
12
|
-
# if using postgresinator, :postgres_container_name will be set by it, so leave this one commented out:
|
13
|
-
#set :postgres_container_name, "my-app-staging.example.com-postgres-5432-master"
|
14
|
-
set :postgres_port, "5432"
|
15
|
-
set :ruby_image_name, "snarlysodboxer/ruby:1.9.3-p547"
|
16
|
-
set :ruby_container_name, "my-app-staging.example.com-ruby-bluepill"
|
17
|
-
set :ruby_container_max_mem_mb, "1024"
|
18
|
-
set :bundler_version, "1.7.4"
|
19
|
-
#set :use_cadvisor, true # this is default
|
@@ -1,50 +0,0 @@
|
|
1
|
-
# Use `cap <stage> deploy from_local=true` to deploy your
|
2
|
-
# locally changed code instead of the code in the git repo. You can also add --trace.
|
3
|
-
if ENV['from_local']
|
4
|
-
set :scm, :copy
|
5
|
-
else
|
6
|
-
set :scm, :git
|
7
|
-
end
|
8
|
-
|
9
|
-
## The values in this file are not meant to be changed and shouldn't
|
10
|
-
## need to be under the majority of circumstances:
|
11
|
-
|
12
|
-
#set :bundle_roles, :all # this is default
|
13
|
-
#set :bundle_servers, -> { release_roles(fetch(:bundle_roles)) } # this is default
|
14
|
-
#set :bundle_binstubs, -> { shared_path.join('bin') } # this is default
|
15
|
-
set :bundle_binstubs, -> { shared_path.join('bundle', 'bin') } # this is required for deployinator
|
16
|
-
set :bundle_gemfile, -> { release_path.join('Gemfile') } # this is required for deployinator
|
17
|
-
#set :bundle_path, -> { shared_path.join('bundle') } # this is default
|
18
|
-
#set :bundle_without, %w{development test}.join(' ') # this is default
|
19
|
-
set :bundle_without, %w{development test deployment}.join(' ')
|
20
|
-
#set :bundle_flags, '--deployment --quiet' # this is default
|
21
|
-
#set :bundle_flags, '--deployment'
|
22
|
-
#set :bundle_env_variables, {} # this is default
|
23
|
-
|
24
|
-
set :docker_run_bluepill_command, -> { [
|
25
|
-
"--tty", "--detach",
|
26
|
-
"--name", fetch(:ruby_container_name),
|
27
|
-
"-e", "GEM_HOME=#{fetch(:deploy_to)}/shared/bundle",
|
28
|
-
"-e", "GEM_PATH=#{fetch(:deploy_to)}/shared/bundle",
|
29
|
-
"-e", "BUNDLE_GEMFILE=#{fetch(:deploy_to)}/current/Gemfile",
|
30
|
-
"-e", "PATH=#{fetch(:deploy_to)}/shared/bundle/bin:$PATH",
|
31
|
-
"--link", "#{fetch(:postgres_container_name)}:postgres",
|
32
|
-
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
33
|
-
"--volume", "#{fetch(:external_socket_path)}:#{fetch(:internal_socket_path)}:rw",
|
34
|
-
"--entrypoint", "#{fetch(:deploy_to)}/shared/bundle/bin/bluepill",
|
35
|
-
"--restart", "always", "--memory", "#{fetch(:ruby_container_max_mem_mb)}m",
|
36
|
-
fetch(:ruby_image_name), "load",
|
37
|
-
"#{fetch(:deploy_to)}/current/config/#{fetch(:application)}_bluepill.rb"
|
38
|
-
] }
|
39
|
-
|
40
|
-
set :docker_run_cadvisor_command, -> { [
|
41
|
-
"--detach",
|
42
|
-
"--name", "cadvisor",
|
43
|
-
"--volume", "/:/rootfs:ro",
|
44
|
-
"--volume", "/var/run:/var/run:rw",
|
45
|
-
"--volume", "/sys:/sys:ro",
|
46
|
-
"--volume", "/var/lib/docker/:/var/lib/docker:ro",
|
47
|
-
"--publish", "127.0.0.1:8080:8080",
|
48
|
-
"--restart", "always",
|
49
|
-
"google/cadvisor:latest"
|
50
|
-
] }
|