dockerfile-rails 1.2.5 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/DEMO.md +1 -1
- data/README.md +11 -3
- data/Rakefile +1 -1
- data/lib/generators/dockerfile_generator.rb +141 -14
- data/lib/generators/templates/Dockerfile.erb +55 -18
- data/lib/generators/templates/_passenger.erb +28 -0
- data/lib/generators/templates/docker-entrypoint.erb +13 -3
- data/lib/generators/templates/litefs.yml.erb +116 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d1b0d5e237b0a8c33a3b1263d34590db8912967420863332ad0b2c8f5661e5f
|
4
|
+
data.tar.gz: 000fd8db20304bc2ba8623aaa1a4bd19870ec4143cacc3cb6ea31b8690510782
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64b8702f00462a318af21238617528682be3831da82471bff0172ef9ef98d7e4a6a10346ffe297e87e76cc5d4b2699bf56b79d9257f1eee3c8cc24bc56012037
|
7
|
+
data.tar.gz: 9a9dc774d388e4276b08bbcdcce9ffd676800df7bb86b4cac34e6795fd0ee6f24ba2a57df393f7948f738a9bdafe8a1ed8073c0b91d9e6a2f9f94349a7d14fe4
|
data/DEMO.md
CHANGED
@@ -132,7 +132,7 @@ docker compose up
|
|
132
132
|
# Demo 4 - API only
|
133
133
|
|
134
134
|
This demo deploys a [Create React App](https://create-react-app.dev/) client and a Rails API-only server. Ruby and Rails version information is retrieved from the server and displayed below a spinning React logo. Note that the build process installs the
|
135
|
-
node
|
135
|
+
node modules and ruby gems in parallel.
|
136
136
|
|
137
137
|
```bash
|
138
138
|
rails new demo --api
|
data/README.md
CHANGED
@@ -40,6 +40,7 @@ bin/rails generate dockerfile
|
|
40
40
|
|
41
41
|
* `--ci` - include test gems in deployed image
|
42
42
|
* `--compose` - generate a `docker-compose.yml` file
|
43
|
+
* `--max-idle=n` - exit afer *n* seconds of inactivity. Supports [iso 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) and [sleep](https://man7.org/linux/man-pages/man1/sleep.1.html#DESCRIPTION) syntaxes. Uses passenger for now, awaiting [puma](https://github.com/puma/puma/issues/2580) support.
|
43
44
|
* `--nginx` - serve static files via [nginx](https://www.nginx.com/). May require `--root` on some targets to access `/dev/stdout`
|
44
45
|
* `--no-link` - don't add [--link](https://docs.docker.com/engine/reference/builder/#copy---link) to COPY statements. Some tools (like at the moment, [buildah](https://www.redhat.com/en/topics/containers/what-is-buildah)) don't yet support this feature.
|
45
46
|
* `--no-lock` - don't add linux platforms, set `BUNDLE_DEPLOY`, or `--frozen-lockfile`. May be needed at times to work around a [rubygems bug](https://github.com/rubygems/rubygems/issues/6082#issuecomment-1329756343).
|
@@ -51,6 +52,7 @@ Generally the dockerfile generator will be able to determine what dependencies y
|
|
51
52
|
are actually using. But should you be using DATABASE_URL, for example, at runtime
|
52
53
|
additional support may be needed:
|
53
54
|
|
55
|
+
* `--litefs` - use [LiteFS](https://fly.io/docs/litefs/)
|
54
56
|
* `--mysql` - add mysql libraries
|
55
57
|
* `--postgresql` - add postgresql libraries
|
56
58
|
* `--redis` - add redis libraries
|
@@ -65,16 +67,18 @@ Not all of your needs can be determined by scanning your application. For examp
|
|
65
67
|
* `--env=name:value` - add an environment variable
|
66
68
|
* `--remove package...` - remove package from "to be added" list
|
67
69
|
|
68
|
-
Each of these can be tailored to a specific build phase by adding `-base`, `-build`, or `-deploy` after the flag name (e.g `--
|
70
|
+
Each of these can be tailored to a specific build phase by adding `-base`, `-build`, or `-deploy` after the flag name (e.g `--add-build freetds-dev --add-deploy freetds-bin`). If no such suffix is found, the default for arg is `-base`, and the default for the rest is `-deploy`. Removal of an arg or environment variable is done by leaving the value blank (e.g `--env-build=PORT:`).
|
69
71
|
|
70
72
|
### Configuration:
|
71
73
|
|
72
74
|
* `--bin-cd` - adjust binstubs to set current working directory
|
75
|
+
[autocrlf](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#_core_autocrlf) enabled or may not be able to set bin stubs as executable.
|
73
76
|
* `--label=name:value` - specify docker label. Can be used multiple times. See [LABEL](https://docs.docker.com/engine/reference/builder/#label) for detail
|
74
77
|
* `--no-prepare` - omit `db:prepare`. Useful for cloud platforms with [release](https://devcenter.heroku.com/articles/release-phase) phases
|
75
78
|
* `--platform=s` - specify target platform. See [FROM](https://docs.docker.com/engine/reference/builder/#from) for details
|
76
79
|
* `--precompile=defer` - may be needed when your configuration requires access to secrets that are not available at build time. Results in larger images and slower deployments.
|
77
80
|
* `--root` - run application as root
|
81
|
+
* `--windows` - make Dockerfile work for Windows users that may have set `git config --global core.autocrlf true`
|
78
82
|
|
79
83
|
Options are saved between runs into `config/dockerfile.yml`. To invert a boolean options, add or remove a `no-` prefix from the option name.
|
80
84
|
|
@@ -107,9 +111,9 @@ If you are running a single test, the following environment variables settings m
|
|
107
111
|
* `TEST_CAPTURE=1` will capture test results.
|
108
112
|
* `TEST_KEEP=1` will leave the test app behind for inspection after the test completes.
|
109
113
|
|
110
|
-
## Links
|
114
|
+
## Historical Links
|
111
115
|
|
112
|
-
The following links relate to the
|
116
|
+
The following links relate to the coordination between this package and Rails 7.1.
|
113
117
|
|
114
118
|
* [Preparations for Rails 7.1](https://community.fly.io/t/preparations-for-rails-7-1/9512) - [Fly.io](https://fly.io/)'s plans and initial discussions with DHH
|
115
119
|
* [Rails Dockerfile futures](https://discuss.rubyonrails.org/t/rails-dockerfile-futures/82091/1) - rationale for a generator
|
@@ -118,3 +122,7 @@ The following links relate to the current development status with respect to Rai
|
|
118
122
|
* Fly.io [Cut over to Rails Dockerfile Generator on Sunday 29 Jan 2023](https://community.fly.io/t/cut-over-to-rails-dockerfile-generator-on-sunday-29-jan-2023/10350)
|
119
123
|
* Fly.io [FAQ](https://fly.io/docs/rails/getting-started/dockerfiles/)
|
120
124
|
* DDH's [target](https://github.com/rails/rails/pull/47372#issuecomment-1438971730)
|
125
|
+
|
126
|
+
Parallel efforts for Hanami:
|
127
|
+
|
128
|
+
* [Proposal](https://discourse.hanamirb.org/t/dockerfile-hanami/816)
|
data/Rakefile
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "erb"
|
4
|
+
require "json"
|
4
5
|
require_relative "../dockerfile-rails/scanner.rb"
|
5
6
|
|
6
7
|
class DockerfileGenerator < Rails::Generators::Base
|
@@ -15,10 +16,13 @@ class DockerfileGenerator < Rails::Generators::Base
|
|
15
16
|
"jemalloc" => false,
|
16
17
|
"label" => {},
|
17
18
|
"link" => true,
|
19
|
+
"litefs" => false,
|
18
20
|
"lock" => true,
|
21
|
+
"max-idle" => nil,
|
19
22
|
"mysql" => false,
|
20
23
|
"nginx" => false,
|
21
24
|
"parallel" => false,
|
25
|
+
"passenger" => false,
|
22
26
|
"platform" => nil,
|
23
27
|
"postgresql" => false,
|
24
28
|
"precompile" => nil,
|
@@ -28,6 +32,7 @@ class DockerfileGenerator < Rails::Generators::Base
|
|
28
32
|
"sqlite3" => false,
|
29
33
|
"sudo" => false,
|
30
34
|
"swap" => nil,
|
35
|
+
"windows" => false,
|
31
36
|
"yjit" => false,
|
32
37
|
}.then { |hash| Struct.new(*hash.keys.map(&:to_sym)).new(*hash.values) }
|
33
38
|
|
@@ -84,6 +89,9 @@ class DockerfileGenerator < Rails::Generators::Base
|
|
84
89
|
class_option "bin-cd", type: :boolean, default: OPTION_DEFAULTS["bin-cd"],
|
85
90
|
desc: "modify binstubs to set working directory"
|
86
91
|
|
92
|
+
class_option "windows", type: :boolean, default: OPTION_DEFAULTS["windows"],
|
93
|
+
desc: "fixup CRLF in binstubs and make each executable"
|
94
|
+
|
87
95
|
class_option :cache, type: :boolean, default: OPTION_DEFAULTS.cache,
|
88
96
|
desc: "use build cache to speed up installs"
|
89
97
|
|
@@ -105,6 +113,9 @@ class DockerfileGenerator < Rails::Generators::Base
|
|
105
113
|
class_option :sqlite3, aliases: "--sqlite", type: :boolean, default: OPTION_DEFAULTS.sqlite3,
|
106
114
|
desc: "include sqlite3 libraries"
|
107
115
|
|
116
|
+
class_option :litefs, type: :boolean, default: OPTION_DEFAULTS.litefs,
|
117
|
+
desc: "replicate sqlite3 databases using litefs"
|
118
|
+
|
108
119
|
class_option :postgresql, aliases: "--postgres", type: :boolean, default: OPTION_DEFAULTS.postgresql,
|
109
120
|
desc: "include postgresql libraries"
|
110
121
|
|
@@ -129,6 +140,12 @@ class DockerfileGenerator < Rails::Generators::Base
|
|
129
140
|
class_option :nginx, type: :boolean, default: OPTION_DEFAULTS.nginx,
|
130
141
|
desc: "Serve static files with nginx"
|
131
142
|
|
143
|
+
class_option :passenger, type: :boolean, default: OPTION_DEFAULTS.passenger,
|
144
|
+
desc: "Serve Rails application with Phusion Passsenger"
|
145
|
+
|
146
|
+
class_option "max-idle", type: :string, default: OPTION_DEFAULTS["max-idle"],
|
147
|
+
desc: "Exit server after application has been idle for n seconds."
|
148
|
+
|
132
149
|
class_option :root, type: :boolean, default: OPTION_DEFAULTS.root,
|
133
150
|
desc: "Run application as root user"
|
134
151
|
|
@@ -221,6 +238,12 @@ class DockerfileGenerator < Rails::Generators::Base
|
|
221
238
|
|
222
239
|
template "docker-compose.yml.erb", "docker-compose.yml" if options.compose
|
223
240
|
|
241
|
+
if using_litefs?
|
242
|
+
template "litefs.yml.erb", "config/litefs.yml"
|
243
|
+
|
244
|
+
fly_attach_consul
|
245
|
+
end
|
246
|
+
|
224
247
|
if @gemfile.include?("vite_ruby")
|
225
248
|
package = JSON.load_file("package.json")
|
226
249
|
unless package.dig("scripts", "build")
|
@@ -284,12 +307,27 @@ private
|
|
284
307
|
options.root?
|
285
308
|
end
|
286
309
|
|
310
|
+
def using_litefs?
|
311
|
+
options.litefs?
|
312
|
+
end
|
313
|
+
|
287
314
|
def using_node?
|
288
315
|
return @using_node if @using_node != nil
|
289
316
|
@using_node = File.exist? "package.json"
|
290
317
|
end
|
291
318
|
|
292
319
|
def using_redis?
|
320
|
+
# Note: If you have redis installed on your computer, 'rails new` will
|
321
|
+
# automatically add redis to your Gemfile, so having it in your Gemfile is
|
322
|
+
# not a reliable indicator of whether or not your application actually uses
|
323
|
+
# redis.
|
324
|
+
|
325
|
+
# using_redis? is currently used for two things: actually adding the redis
|
326
|
+
# gem if it is going to be needed in production, and adding a redis
|
327
|
+
# container to docker-compose.yml. Neither of these actions should be done
|
328
|
+
# unless there is an indication that redis is actually being used and not
|
329
|
+
# merely included in the Gemfile.
|
330
|
+
|
293
331
|
options.redis? or @redis or @gemfile.include?("sidekiq")
|
294
332
|
end
|
295
333
|
|
@@ -301,6 +339,10 @@ private
|
|
301
339
|
@gemfile.include?("grover") or @gemfile.include?("puppeteer-ruby")
|
302
340
|
end
|
303
341
|
|
342
|
+
def using_passenger?
|
343
|
+
options.passenger? or options["max-idle"]
|
344
|
+
end
|
345
|
+
|
304
346
|
def using_sidekiq?
|
305
347
|
@gemfile.include?("sidekiq")
|
306
348
|
end
|
@@ -369,7 +411,9 @@ private
|
|
369
411
|
gems = ["bundler"]
|
370
412
|
|
371
413
|
if options.ci? && options.lock? && @gemfile.include?("debug")
|
372
|
-
|
414
|
+
# https://github.com/rails/rails/pull/47515
|
415
|
+
# https://github.com/rubygems/rubygems/issues/6082#issuecomment-1329756343
|
416
|
+
gems += %w(irb reline) - @gemfile unless Gem.ruby_version >= "3.2.2"
|
373
417
|
end
|
374
418
|
|
375
419
|
gems.sort
|
@@ -391,7 +435,6 @@ private
|
|
391
435
|
# libicu63 in buster, libicu67 in bullseye, libiclu72 in bookworm...
|
392
436
|
packages << "libicu-dev" if @gemfile.include? "charlock_holmes"
|
393
437
|
|
394
|
-
|
395
438
|
if @gemfile.include? "webp-ffi"
|
396
439
|
# https://github.com/le0pard/webp-ffi#requirements
|
397
440
|
packages += %w(libjpeg-dev libpng-dev libtiff-dev libwebp-dev)
|
@@ -464,7 +507,11 @@ private
|
|
464
507
|
# start with databases: sqlite3, postgres, mysql
|
465
508
|
packages << "libsqlite3-0" if options.sqlite3? || @sqlite3
|
466
509
|
packages << "postgresql-client" if options.postgresql? || @postgresql
|
467
|
-
packages << "default-mysql-client" if options.mysql || @mysql
|
510
|
+
packages << "default-mysql-client" if options.mysql? || @mysql
|
511
|
+
packages << "libjemalloc2" if options.jemalloc? && !options.fullstaq?
|
512
|
+
|
513
|
+
# litefs
|
514
|
+
packages += ["ca-certificates", "fuse3", "sudo"] if options.litefs?
|
468
515
|
|
469
516
|
# ActiveStorage preview support
|
470
517
|
packages << "libvips" if @gemfile.include? "ruby-vips"
|
@@ -483,8 +530,11 @@ private
|
|
483
530
|
end
|
484
531
|
end
|
485
532
|
|
533
|
+
# Passenger
|
534
|
+
packages += %w(passenger libnginx-mod-http-passenger) if using_passenger?
|
535
|
+
|
486
536
|
# nginx
|
487
|
-
packages << "nginx" if options.nginx?
|
537
|
+
packages << "nginx" if options.nginx? || using_passenger?
|
488
538
|
|
489
539
|
# sudo
|
490
540
|
packages << "sudo" if options.sudo?
|
@@ -494,18 +544,35 @@ private
|
|
494
544
|
|
495
545
|
def deploy_repos
|
496
546
|
repos = []
|
547
|
+
packages = []
|
497
548
|
|
498
549
|
if using_puppeteer? && deploy_packages.include?("google-chrome-stable")
|
550
|
+
packages += %w(gnupg curl)
|
499
551
|
repos += [
|
500
|
-
"curl https://
|
501
|
-
"gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg &&",
|
552
|
+
"curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key.txt |",
|
553
|
+
" gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg &&",
|
502
554
|
'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
|
503
555
|
]
|
504
556
|
end
|
505
557
|
|
558
|
+
if using_passenger?
|
559
|
+
packages += %w(gnupg curl)
|
560
|
+
repos += [
|
561
|
+
"curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key.txt |",
|
562
|
+
" gpg --dearmor > /etc/apt/trusted.gpg.d/phusion.gpg &&",
|
563
|
+
"bash -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger $(source /etc/os-release; echo $VERSION_CODENAME) main > /etc/apt/sources.list.d/passenger.list'"
|
564
|
+
]
|
565
|
+
end
|
566
|
+
|
506
567
|
if repos.empty?
|
507
568
|
""
|
508
569
|
else
|
570
|
+
packages.sort!.uniq!
|
571
|
+
unless packages.empty?
|
572
|
+
repos.unshift "apt-get update -qq &&",
|
573
|
+
"apt-get install --no-install-recommends -y #{packages.join(" ")} &&"
|
574
|
+
end
|
575
|
+
|
509
576
|
repos.join(" \\\n ") + " && \\\n "
|
510
577
|
end
|
511
578
|
end
|
@@ -552,18 +619,26 @@ private
|
|
552
619
|
def deploy_env
|
553
620
|
env = {}
|
554
621
|
|
555
|
-
env["PORT"] = "3001" if options.nginx?
|
622
|
+
env["PORT"] = "3001" if (options.nginx? && !using_passenger?) || using_litefs?
|
556
623
|
|
557
624
|
if Rails::VERSION::MAJOR < 7 || Rails::VERSION::STRING.start_with?("7.0")
|
558
625
|
env["RAILS_LOG_TO_STDOUT"] = "1"
|
559
626
|
env["RAILS_SERVE_STATIC_FILES"] = "true" unless options.nginx?
|
560
627
|
end
|
561
628
|
|
629
|
+
if deploy_database == "sqlite3"
|
630
|
+
if using_litefs?
|
631
|
+
env["DATABASE_URL"] = "sqlite3:///litefs/production.sqlite3"
|
632
|
+
else
|
633
|
+
env["DATABASE_URL"] = "sqlite3:///data/production.sqlite3"
|
634
|
+
end
|
635
|
+
end
|
636
|
+
|
562
637
|
if options.yjit?
|
563
638
|
env["RUBY_YJIT_ENABLE"] = "1"
|
564
639
|
end
|
565
640
|
|
566
|
-
if options.jemalloc? &&
|
641
|
+
if options.jemalloc? && !options.fullstaq?
|
567
642
|
if (options.platform || Gem::Platform.local.cpu).include? "arm"
|
568
643
|
env["LD_PRELOAD"] = "/usr/lib/aarch64-linux-gnu/libjemalloc.so.2"
|
569
644
|
else
|
@@ -590,7 +665,7 @@ private
|
|
590
665
|
|
591
666
|
env.merge! @@vars["base"] if @@vars["base"]
|
592
667
|
|
593
|
-
env.map { |key, value| "#{key}=#{value.inspect}" }
|
668
|
+
env.map { |key, value| "#{key}=#{value.inspect}" }.sort
|
594
669
|
end
|
595
670
|
|
596
671
|
def base_args
|
@@ -648,13 +723,13 @@ private
|
|
648
723
|
# none are required, but prepares for the need to do the
|
649
724
|
# fix line endings if other fixups are required.
|
650
725
|
has_cr = Dir["bin/*"].any? { |file| IO.read(file).include? "\r" }
|
651
|
-
if has_cr || (Gem.win_platform? && !binfixups.empty?)
|
726
|
+
if has_cr || (Gem.win_platform? && !binfixups.empty?) || options.windows?
|
652
727
|
binfixups.unshift 'sed -i "s/\r$//g" bin/*'
|
653
728
|
end
|
654
729
|
|
655
730
|
# Windows file systems may not have the concept of executable.
|
656
731
|
# In such cases, fix up during the build.
|
657
|
-
|
732
|
+
if Dir["bin/*"].any? { |file| !File.executable?(file) } || options.windows?
|
658
733
|
binfixups.unshift "chmod +x bin/*"
|
659
734
|
end
|
660
735
|
|
@@ -667,9 +742,11 @@ private
|
|
667
742
|
end
|
668
743
|
|
669
744
|
def deploy_database
|
670
|
-
|
745
|
+
# note: as database can be overridden at runtime via DATABASE_URL,
|
746
|
+
# use presence of "pg" or "mysql2" in the bundle as evidence of intent.
|
747
|
+
if options.postgresql? || @postgresql || @gemfile.include?("pg")
|
671
748
|
"postgresql"
|
672
|
-
elsif options.mysql || @mysql
|
749
|
+
elsif options.mysql? || @mysql || @gemfile.include?("mysql2")
|
673
750
|
"mysql"
|
674
751
|
else
|
675
752
|
"sqlite3"
|
@@ -758,7 +835,11 @@ private
|
|
758
835
|
end
|
759
836
|
|
760
837
|
def procfile
|
761
|
-
if
|
838
|
+
if using_passenger?
|
839
|
+
{
|
840
|
+
nginx: "nginx"
|
841
|
+
}
|
842
|
+
elsif options.nginx?
|
762
843
|
{
|
763
844
|
nginx: '/usr/sbin/nginx -g "daemon off;"',
|
764
845
|
rails: "./bin/rails server -p 3001"
|
@@ -782,4 +863,50 @@ private
|
|
782
863
|
|
783
864
|
more
|
784
865
|
end
|
866
|
+
|
867
|
+
def max_idle
|
868
|
+
option = options["max-idle"]
|
869
|
+
|
870
|
+
if option == nil || option.strip.downcase == "infinity"
|
871
|
+
nil
|
872
|
+
elsif /^\s*\d+(\.\d+)\s*/.match? option
|
873
|
+
option.to_f
|
874
|
+
elsif /^\s*P/.match? option
|
875
|
+
ActiveSupport::Duration.parse(option.strip).seconds
|
876
|
+
else
|
877
|
+
option.scan(/\d+\w/).map do |t|
|
878
|
+
ActiveSupport::Duration.parse("PT#{t.upcase}") rescue ActiveSupport::Duration.parse("P#{t.upcase}")
|
879
|
+
end.sum.seconds
|
880
|
+
end
|
881
|
+
rescue ArgumentError
|
882
|
+
nil
|
883
|
+
end
|
884
|
+
|
885
|
+
# if running on fly v2, make a best effort to attach consul
|
886
|
+
def fly_attach_consul
|
887
|
+
# certainly not fly unless there is a fly.toml
|
888
|
+
return unless File.exist? "fly.toml"
|
889
|
+
|
890
|
+
# Check fly.toml to guess if v1 or v2
|
891
|
+
toml = File.read("fly.toml")
|
892
|
+
return if toml.include?("enable_consul") # v1-ism
|
893
|
+
return unless toml.include?("primary_region") # v2
|
894
|
+
|
895
|
+
# see if flyctl is in the path
|
896
|
+
paths = ENV["PATH"].split(File::PATH_SEPARATOR)
|
897
|
+
cmds = %w(flyctl)
|
898
|
+
exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
|
899
|
+
flyctl = Enumerator.product(paths, cmds, exts).
|
900
|
+
map { |path, cmd, ext| File.join(path, "#{cmd}#{ext}") }.
|
901
|
+
find { |path| File.executable? path }
|
902
|
+
return unless flyctl
|
903
|
+
|
904
|
+
# see if secret is already set?
|
905
|
+
secrets = JSON.parse(`#{flyctl} secrets list --json`)
|
906
|
+
return if secrets.any? { |secret| secret["Name"] == "FLY_CONSUL_URL" }
|
907
|
+
|
908
|
+
# attach consul
|
909
|
+
say_status "execute", "flyctl consul attach", :green
|
910
|
+
system "#{flyctl} consul attach"
|
911
|
+
end
|
785
912
|
end
|
@@ -134,6 +134,12 @@ RUN SECRET_KEY_BASE<%= Rails::VERSION::MAJOR<7 || Rails::VERSION::STRING.start_w
|
|
134
134
|
# Final stage for app image
|
135
135
|
FROM base
|
136
136
|
|
137
|
+
<% end -%>
|
138
|
+
<% if using_litefs? -%>
|
139
|
+
# Install, configure litefs
|
140
|
+
COPY --from=flyio/litefs:0.4.0 /usr/local/bin/litefs /usr/local/bin/litefs
|
141
|
+
COPY<% if options.link? %> --link<% end %> config/litefs.yml /etc/litefs.yml
|
142
|
+
|
137
143
|
<% end -%>
|
138
144
|
<% unless deploy_args.empty? -%>
|
139
145
|
# Deployment build arguments
|
@@ -145,38 +151,63 @@ ARG <%= deploy_args.map {|key, value| "#{key}=#{value.inspect}"}.join(" \\\n
|
|
145
151
|
<%= render partial: 'apt_install', locals: {packages: deploy_packages, clean: true, repos: deploy_repos} %>
|
146
152
|
<% end -%>
|
147
153
|
|
148
|
-
<% if
|
154
|
+
<% if using_passenger? -%>
|
155
|
+
<%= render partial: 'passenger' %>
|
156
|
+
|
157
|
+
<% elsif options.nginx? -%>
|
149
158
|
<%= render partial: 'nginx' %>
|
150
159
|
|
151
160
|
<% elsif procfile.size > 1 -%>
|
152
161
|
RUN gem install foreman
|
153
162
|
|
154
163
|
<% end -%>
|
155
|
-
<% unless
|
156
|
-
#
|
164
|
+
<% unless options.precompile == "defer" -%>
|
165
|
+
# Copy built artifacts: gems, application
|
166
|
+
COPY --from=build /usr/local/bundle /usr/local/bundle
|
167
|
+
COPY --from=build /rails /rails
|
168
|
+
<% if api_client_dir -%>
|
169
|
+
|
170
|
+
# Copy built client
|
171
|
+
COPY --from=client /rails/<%= api_client_dir %>/build /rails/public
|
172
|
+
<% end -%>
|
173
|
+
|
174
|
+
<% end -%>
|
175
|
+
<% if run_as_root? -%>
|
176
|
+
<% if deploy_database == 'sqlite3' -%>
|
177
|
+
RUN mkdir /data
|
178
|
+
<% end -%>
|
179
|
+
<% else -%>
|
180
|
+
# Run and own only the runtime files as a non-root user for security
|
157
181
|
<% if options.compose? -%>
|
158
182
|
ARG UID=1000 \
|
159
183
|
GID=1000
|
160
184
|
RUN groupadd -f -g $GID rails && \
|
161
|
-
useradd -u $UID -g $GID rails
|
162
|
-
|
163
|
-
|
185
|
+
useradd -u $UID -g $GID rails --create-home --shell /bin/bash && \
|
186
|
+
<% else -%>
|
187
|
+
RUN useradd rails --create-home --shell /bin/bash && \
|
188
|
+
<% end -%>
|
189
|
+
<% if options.nginx? -%>
|
190
|
+
chown rails:rails /var/lib/nginx /var/log/nginx/* && \
|
191
|
+
<% end -%>
|
192
|
+
<% if deploy_packages.include?("sudo") && options.sudo? -%>
|
164
193
|
sed -i 's/env_reset/env_keep="*"/' /etc/sudoers && \
|
165
|
-
chown rails:rails .<% end %>
|
166
|
-
<% unless options.swap -%>
|
167
|
-
USER rails:rails
|
168
194
|
<% end -%>
|
169
|
-
|
195
|
+
<% if deploy_database == 'sqlite3' -%>
|
196
|
+
mkdir /data<% if using_litefs? %> /litefs<% end %> && \
|
197
|
+
chown -R rails:rails <%= Dir[*%w(db log storage tmp)].join(" ") %> /data<% if using_litefs? %> /litefs<% end %>
|
198
|
+
<% else -%>
|
199
|
+
chown -R rails:rails <%= Dir[*%w(db log storage tmp)].join(" ") %>
|
200
|
+
<% end -%>
|
201
|
+
<% unless options.swap? or using_passenger? or using_litefs? -%>
|
202
|
+
USER rails:rails
|
170
203
|
<% end -%>
|
171
|
-
<% unless options.precompile == "defer" -%>
|
172
|
-
# Copy built artifacts: gems, application
|
173
|
-
COPY --from=build /usr/local/bundle /usr/local/bundle
|
174
|
-
COPY --from=build <% unless run_as_root? %>--chown=rails:rails <% end %>/rails /rails
|
175
|
-
<% if api_client_dir -%>
|
176
204
|
|
177
|
-
# Copy built client
|
178
|
-
COPY --from=client <% unless run_as_root? %>--chown=rails:rails <% end %>/rails/<%= api_client_dir %>/build /rails/public
|
179
205
|
<% end -%>
|
206
|
+
<% if using_litefs? and !run_as_root? -%>
|
207
|
+
# Authorize rails user to launch litefs
|
208
|
+
COPY <<-"EOF" /etc/sudoers.d/rails
|
209
|
+
rails ALL=(root) /usr/local/bin/litefs
|
210
|
+
EOF
|
180
211
|
|
181
212
|
<% end -%>
|
182
213
|
<% unless deploy_env.empty? -%>
|
@@ -201,9 +232,15 @@ EOF
|
|
201
232
|
|
202
233
|
# Start the server by default, this can be overwritten at runtime
|
203
234
|
EXPOSE 3000
|
235
|
+
<% if deploy_database == 'sqlite3' -%>
|
236
|
+
VOLUME /data
|
237
|
+
<% end -%>
|
204
238
|
CMD ["foreman", "start", "--procfile=Procfile.prod"]
|
205
239
|
<% else -%>
|
206
240
|
# Start the server by default, this can be overwritten at runtime
|
207
241
|
EXPOSE 3000
|
208
|
-
|
242
|
+
<% if deploy_database == 'sqlite3' -%>
|
243
|
+
VOLUME /data
|
244
|
+
<% end -%>
|
245
|
+
CMD <%= procfile.values.first.split(" ").inspect %>
|
209
246
|
<% end -%>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# configure nginx and passenger
|
2
|
+
COPY <<-'EOF' /etc/nginx/sites-enabled/default
|
3
|
+
server {
|
4
|
+
listen 3000;
|
5
|
+
root /rails/public;
|
6
|
+
passenger_enabled on;
|
7
|
+
<% if options['max-idle'] -%>
|
8
|
+
passenger_ctl hook_detached_process /etc/nginx/hook_detached_process;
|
9
|
+
passenger_min_instances 0;
|
10
|
+
passenger_pool_idle_time <%= max_idle %>;
|
11
|
+
<% end -%>
|
12
|
+
}
|
13
|
+
<% if options['max-idle'] -%>
|
14
|
+
COPY <<-'EOF' /etc/nginx/sites-enabled/hook_detached_process
|
15
|
+
#!/usr/bin/env ruby
|
16
|
+
status = `passenger-status`
|
17
|
+
processes = status[/^Processes\s*:\s*(\d*)/, 1].to_i
|
18
|
+
system 'nginx -s stop' if processes == 0
|
19
|
+
EOF
|
20
|
+
<% end -%>
|
21
|
+
EOF
|
22
|
+
RUN echo "daemon off;" >> /etc/nginx/nginx.conf && \
|
23
|
+
sed -i 's/access_log\s.*;/access_log \/dev\/stdout;/' /etc/nginx/nginx.conf && \
|
24
|
+
sed -i 's/error_log\s.*;/error_log \/dev\/stderr info;/' /etc/nginx/nginx.conf && \
|
25
|
+
<% if options['max-idle'] -%>
|
26
|
+
chmod +sx /etc/nginx/sites-enabled/hook_detached_process && \
|
27
|
+
<% end -%>
|
28
|
+
mkdir /var/run/passenger-instreg
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/bin/bash -e
|
2
2
|
|
3
3
|
<% if options.swap -%>
|
4
|
-
<% if run_as_root? -%>
|
4
|
+
<% if run_as_root? or using_passenger? -%>
|
5
5
|
<% @space = "" -%>
|
6
6
|
<% else -%>
|
7
7
|
<% @space = " " -%>
|
@@ -14,11 +14,21 @@ if [ $UID -eq 0 ]; then
|
|
14
14
|
<%= @space %>echo 10 > /proc/sys/vm/swappiness
|
15
15
|
<%= @space %>swapon /swapfile
|
16
16
|
<%= @space %>echo 1 > /proc/sys/vm/overcommit_memory
|
17
|
-
<%
|
17
|
+
<% if using_litefs? -%>
|
18
|
+
|
19
|
+
<%= @space %># mount litefs
|
20
|
+
<%= @space %>litefs mount &
|
21
|
+
<% end -%>
|
22
|
+
<% unless run_as_root? or using_passenger? -%>
|
23
|
+
|
18
24
|
exec su rails $0 $@
|
19
25
|
fi
|
20
26
|
<% end -%>
|
21
27
|
|
28
|
+
<% elsif using_litefs? -%>
|
29
|
+
# mount litefs
|
30
|
+
<% unless run_as_root? %>sudo -E <% end %>litefs mount &
|
31
|
+
|
22
32
|
<% end -%>
|
23
33
|
<% if options.prepare -%>
|
24
34
|
<% if procfile.size > 1 -%>
|
@@ -26,7 +36,7 @@ fi
|
|
26
36
|
if [ "${*}" == "foreman start --procfile=Procfile.prod" ]; then
|
27
37
|
<% else -%>
|
28
38
|
# If running the rails server then create or migrate existing database
|
29
|
-
if [ "${*}" == "
|
39
|
+
if [ "${*}" == <%= procfile.values.first.inspect %> <% if using_litefs? %>-a "$FLY_REGION" == "$PRIMARY_REGION" <%end%>]; then
|
30
40
|
<% end -%>
|
31
41
|
<% if options.precompile == "defer" -%>
|
32
42
|
./bin/rails assets:precompile
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# based on: https://github.com/superfly/litefs/blob/main/cmd/litefs/etc/litefs.yml
|
2
|
+
|
3
|
+
# The FUSE section handles settings on the FUSE file system. FUSE
|
4
|
+
# provides a layer for intercepting SQLite transactions on the
|
5
|
+
# primary node so they can be shipped to replica nodes transparently.
|
6
|
+
fuse:
|
7
|
+
# Required. This is the mount directory that applications will
|
8
|
+
# use to access their SQLite databases.
|
9
|
+
dir: "/litefs"
|
10
|
+
|
11
|
+
# Set this flag to true to allow non-root users to access mount.
|
12
|
+
# You must set the "user_allow_other" option in /etc/fuse.conf first.
|
13
|
+
allow-other: false
|
14
|
+
|
15
|
+
# The debug flag enables debug logging of all FUSE API calls.
|
16
|
+
# This will produce a lot of logging. Not for general use.
|
17
|
+
debug: false
|
18
|
+
|
19
|
+
# The data section specifies where internal LiteFS data is stored
|
20
|
+
# and how long to retain the transaction files.
|
21
|
+
#
|
22
|
+
# Transaction files are used to ship changes to replica nodes so
|
23
|
+
# they should persist long enough for replicas to retrieve them,
|
24
|
+
# even in the face of a short network interruption or a redeploy.
|
25
|
+
# Under high load, these files can grow large so it's not advised
|
26
|
+
# to extend retention too long.
|
27
|
+
data:
|
28
|
+
# Path to internal data storage.
|
29
|
+
dir: "/data"
|
30
|
+
|
31
|
+
# Duration to keep LTX files. Latest LTX file is always kept.
|
32
|
+
retention: "10m"
|
33
|
+
|
34
|
+
# Frequency with which to check for LTX files to delete.
|
35
|
+
retention-monitor-interval: "1m"
|
36
|
+
|
37
|
+
# If true, then LiteFS will not wait until the node becomes the
|
38
|
+
# primary or connects to the primary before starting the subprocess.
|
39
|
+
skip-sync: false
|
40
|
+
|
41
|
+
# If true, then LiteFS will not exit if there is a validation
|
42
|
+
# issue on startup. This can be useful for debugging issues as
|
43
|
+
# it avoids constantly restarting the node on ephemeral hosting.
|
44
|
+
exit-on-error: false
|
45
|
+
|
46
|
+
# This section defines settings for the LiteFS HTTP API server.
|
47
|
+
# This API server is how nodes communicate with each other.
|
48
|
+
http:
|
49
|
+
# Specifies the bind address of the HTTP API server.
|
50
|
+
addr: ":20202"
|
51
|
+
|
52
|
+
# This section defines settings for the option HTTP proxy.
|
53
|
+
# This proxy can handle primary forwarding & replica consistency
|
54
|
+
# for applications that use a single SQLite database.
|
55
|
+
proxy:
|
56
|
+
# Specifies the bind address of the proxy server.
|
57
|
+
addr: ":3000"
|
58
|
+
|
59
|
+
# The hostport of the target application.
|
60
|
+
target: "localhost:3001"
|
61
|
+
|
62
|
+
# The name of the database used for TXID tracking.
|
63
|
+
db: "production.sqlite3"
|
64
|
+
|
65
|
+
# If true, enables verbose logging of requests by the proxy.
|
66
|
+
debug: false
|
67
|
+
|
68
|
+
# List of paths that are ignored by the proxy. The asterisk is
|
69
|
+
# the only available wildcard. These requests are passed
|
70
|
+
# through to the target as-is.
|
71
|
+
passthrough: []
|
72
|
+
|
73
|
+
# The lease section defines how LiteFS creates a cluster and
|
74
|
+
# implements leader election. For dynamic clusters, use the
|
75
|
+
# "consul". This allows the primary to change automatically when
|
76
|
+
# the current primary goes down. For a simpler setup, use
|
77
|
+
# "static" which assigns a single node to be the primary and does
|
78
|
+
# not failover.
|
79
|
+
lease:
|
80
|
+
# Required. Must be either "consul" or "static".
|
81
|
+
type: "consul"
|
82
|
+
|
83
|
+
# Required. The URL for this node's LiteFS API.
|
84
|
+
# Should match HTTP port.
|
85
|
+
advertise-url: "http://${HOSTNAME}.vm.${FLY_APP_NAME}.internal:20202"
|
86
|
+
|
87
|
+
# Specifies whether the node can become the primary. If using
|
88
|
+
# "static" leasing, this should be set to true on the primary
|
89
|
+
# and false on the replicas.
|
90
|
+
candidate: ${FLY_REGION == PRIMARY_REGION}
|
91
|
+
|
92
|
+
# A Consul server provides leader election and ensures that the
|
93
|
+
# responsibility of the primary node can be moved in the event
|
94
|
+
# of a deployment or a failure.
|
95
|
+
consul:
|
96
|
+
# Required. The base URL of the Consul server.
|
97
|
+
url: "${FLY_CONSUL_URL}"
|
98
|
+
|
99
|
+
# Required. The key used for obtaining a lease by the primary.
|
100
|
+
# This must be unique for each cluster of LiteFS servers
|
101
|
+
key: "litefs/${FLY_APP_NAME}"
|
102
|
+
|
103
|
+
# Length of time before a lease expires. The primary will
|
104
|
+
# automatically renew the lease while it is alive, however,
|
105
|
+
# if it fails to renew in time then a new primary may be
|
106
|
+
# elected after the TTL. This only occurs for unexpected loss
|
107
|
+
# of the leader as normal operation will allow the leader to
|
108
|
+
# handoff the lease to another replica without downtime.
|
109
|
+
#
|
110
|
+
# Consul does not allow a TTL of less than 10 seconds.
|
111
|
+
ttl: "10s"
|
112
|
+
|
113
|
+
# Length of time after the lease expires before a candidate
|
114
|
+
# can become leader. This buffer is intended to prevent
|
115
|
+
# overlap in leadership due to clock skew or in-flight calls.
|
116
|
+
lock-delay: "1s"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dockerfile-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Ruby
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -44,16 +44,18 @@ files:
|
|
44
44
|
- lib/generators/templates/_nginx.erb
|
45
45
|
- lib/generators/templates/_node_client.erb
|
46
46
|
- lib/generators/templates/_npm_install.erb
|
47
|
+
- lib/generators/templates/_passenger.erb
|
47
48
|
- lib/generators/templates/docker-compose.yml.erb
|
48
49
|
- lib/generators/templates/docker-entrypoint.erb
|
49
50
|
- lib/generators/templates/dockerfile.yml.erb
|
50
51
|
- lib/generators/templates/dockerignore.erb
|
52
|
+
- lib/generators/templates/litefs.yml.erb
|
51
53
|
- lib/generators/templates/node-version.erb
|
52
|
-
homepage: https://github.com/
|
54
|
+
homepage: https://github.com/fly-apps/dockerfile-rails
|
53
55
|
licenses:
|
54
56
|
- MIT
|
55
57
|
metadata:
|
56
|
-
homepage_uri: https://github.com/
|
58
|
+
homepage_uri: https://github.com/fly-apps/dockerfile-rails
|
57
59
|
post_install_message:
|
58
60
|
rdoc_options: []
|
59
61
|
require_paths:
|
@@ -69,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
71
|
- !ruby/object:Gem::Version
|
70
72
|
version: '0'
|
71
73
|
requirements: []
|
72
|
-
rubygems_version: 3.4.
|
74
|
+
rubygems_version: 3.4.8
|
73
75
|
signing_key:
|
74
76
|
specification_version: 4
|
75
77
|
summary: Dockerfile generator for Rails
|