deployinator 0.0.7 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
-
] }
|