dockerfile-rails 1.2.5 → 1.4.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.
- 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
|