Skip to content

Commit

Permalink
New Fly.io and Action Cable architecture, including AnyCable
Browse files Browse the repository at this point in the history
  • Loading branch information
RISCfuture committed Sep 14, 2024
1 parent 2d0a209 commit 33ad3f0
Show file tree
Hide file tree
Showing 17 changed files with 172 additions and 58 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up flyctl
uses: superfly/flyctl-actions/setup-flyctl@master
- name: Deploy to Fly.io
Expand Down
46 changes: 24 additions & 22 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,62 +1,64 @@
# syntax = docker/dockerfile:1

# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand:
# docker build -t my-app .
# docker run -d -p 80:80 -p 443:443 --name my-app -e RAILS_MASTER_KEY=<value from config/master.key> my-app

# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.3.5
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base
FROM ruby:$RUBY_VERSION-slim as base

LABEL fly_launch_runtime="rails"

# Rails app lives here
WORKDIR /rails

# Install base packages
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl libjemalloc2 libvips postgresql-client && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Set production environment
ENV RAILS_ENV="production" \
BUNDLE_DEPLOYMENT="1" \
ENV BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development:test"
BUNDLE_WITHOUT="development:test" \
RAILS_ENV="production"

# Update gems and bundler
RUN gem update --system --no-document && \
gem install -N bundler


# Throw-away build stage to reduce size of final image
FROM base AS build
FROM base as build

# Install packages needed to build gems
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git libpq-dev pkg-config libyaml-dev && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
apt-get install --no-install-recommends -y build-essential libpq-dev libvips

# Install application gems
COPY Gemfile Gemfile.lock ./
COPY --link Gemfile Gemfile.lock ./
RUN bundle install && \
bundle exec bootsnap precompile --gemfile && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git

# Copy application code
COPY . .

COPY --link . .

# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/


# Final stage for app image
FROM base

# Install packages needed for deployment
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl imagemagick libvips postgresql-client && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Copy built artifacts: gems, application
COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
COPY --from=build /rails /rails

# Run and own only the runtime files as a non-root user for security
RUN groupadd --system --gid 1000 rails && \
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
chown -R rails:rails db log storage tmp
chown -R 1000:1000 db log storage tmp
USER 1000:1000

# Entrypoint prepares the database.
# Entrypoint sets up the container.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]

# Start the server by default, this can be overwritten at runtime
Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby "3.3.5"

# CORE
gem "bootsnap", require: false
gem "puma"
gem "rails"
gem "responders"

# FRAMEWORK
gem "anycable-rails"
gem "devise"
gem "devise-jwt"
gem "good_job"
Expand Down
37 changes: 36 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,28 @@ GEM
tzinfo (~> 2.0, >= 2.0.5)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
anycable (1.5.1)
anycable-core (= 1.5.1)
grpc (~> 1.53)
anycable-core (1.5.1)
anyway_config (~> 2.2)
google-protobuf (~> 3.25)
anycable-rails (1.5.3)
anycable (~> 1.5.0)
anycable-rails-core (= 1.5.3)
anycable-rails-core (1.5.3)
actioncable (>= 6.0, < 8)
anycable-core (~> 1.5.0)
globalid
anyway_config (2.6.4)
ruby-next-core (~> 1.0)
base64 (0.2.0)
bcrypt (3.1.20)
bigdecimal (3.1.8)
binding_of_caller (1.0.1)
debug_inspector (>= 1.2.0)
bootsnap (1.18.4)
msgpack (~> 1.2)
brakeman (6.2.1)
racc
bugsnag (6.27.1)
Expand Down Expand Up @@ -140,6 +157,20 @@ GEM
fugit (>= 1.11.0)
railties (>= 6.1.0)
thor (>= 1.0.0)
google-protobuf (3.25.4-aarch64-linux)
google-protobuf (3.25.4-arm64-darwin)
google-protobuf (3.25.4-x86_64-linux)
googleapis-common-protos-types (1.16.0)
google-protobuf (>= 3.18, < 5.a)
grpc (1.66.0-aarch64-linux)
google-protobuf (>= 3.25, < 5.0)
googleapis-common-protos-types (~> 1.0)
grpc (1.66.0-arm64-darwin)
google-protobuf (>= 3.25, < 5.0)
googleapis-common-protos-types (~> 1.0)
grpc (1.66.0-x86_64-linux)
google-protobuf (>= 3.25, < 5.0)
googleapis-common-protos-types (~> 1.0)
hashdiff (1.1.1)
i18n (1.14.5)
concurrent-ruby (~> 1.0)
Expand Down Expand Up @@ -169,6 +200,7 @@ GEM
marcel (1.0.4)
mini_mime (1.1.5)
minitest (5.25.1)
msgpack (1.7.2)
net-imap (0.4.16)
date
net-protocol
Expand Down Expand Up @@ -268,6 +300,7 @@ GEM
rspec-mocks (~> 3.13)
rspec-support (~> 3.13)
rspec-support (3.13.1)
ruby-next-core (1.0.3)
securerandom (0.3.1)
stringio (3.1.1)
thor (1.3.2)
Expand Down Expand Up @@ -300,7 +333,9 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
anycable-rails
binding_of_caller
bootsnap
brakeman
bugsnag
database_cleaner
Expand Down Expand Up @@ -329,4 +364,4 @@ RUBY VERSION
ruby 3.3.5p100

BUNDLED WITH
2.5.16
2.5.18
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ such as:
backend: cd Backend && rvm 3.3.5@flyweight exec rails server
frontend: cd Frontend && yarn dev
jobs: cd Backend && rvm 3.3.5@flyweight exec bundle exec good_job start
cable: cd Backend && rvm 3.3.5@flyweight exec ./bin/cable
anycable: cd Backend && rvm 3.3.5@flyweight exec anycable
ws: cd Backend && rvm 3.3.5@flyweight exec bin/anycable-go --port=8080
```

Install the `foreman` gem to run the Procfile.
Expand Down
22 changes: 22 additions & 0 deletions bin/anycable-go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

cd $(dirname $0)/..

# It's recommended to use the exact version of AnyCable here
version="latest"

if [ ! -f ./bin/dist/anycable-go ]; then
echo "AnyCable server is not installed, downloading..."
./bin/rails g anycable:download --version=$version --bin-path=./bin/dist
fi

curVersion=$(./bin/dist/anycable-go -v)

if [[ "$version" != "latest" ]]; then
if [[ "$curVersion" != "$version"* ]]; then
echo "AnyCable server version is not $version, downloading a new one..."
./bin/rails g anycable:download --version=$version --bin-path=./bin/dist
fi
fi

./bin/dist/anycable-go $@
2 changes: 1 addition & 1 deletion bin/cable
Original file line number Diff line number Diff line change
@@ -1 +1 @@
bundle exec puma -p 28080 cable/config.ru --pidfile tmp/pids/cable.pid $*
bundle exec puma -p 8080 cable/config.ru --pidfile tmp/pids/cable.pid $*
Binary file added bin/dist/anycable-go
Binary file not shown.
38 changes: 38 additions & 0 deletions config/anycable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# This file contains per-environment settings for AnyCable.
#
# Since AnyCable config is based on anyway_config (https://github.com/palkan/anyway_config), all AnyCable settings
# can be set or overridden through the corresponding environment variables.
# E.g., `rpc_host` is overridden by ANYCABLE_RPC_HOST, `debug` by ANYCABLE_DEBUG etc.
#
# Note that AnyCable recognizes REDIS_URL env variable for Redis pub/sub adapter. If you want to
# use another Redis instance for AnyCable, provide ANYCABLE_REDIS_URL variable.
#
# Read more about AnyCable configuration here: https://docs.anycable.io/ruby/configuration
#
default: &default
# Turn on/off access logs ("Started..." and "Finished...")
access_logs_disabled: false
# Whether to enable gRPC level logging or not
log_grpc: false
# Use Redis to broadcast messages to AnyCable server
broadcast_adapter: redis
# You can use REDIS_URL env var to configure Redis URL.
# Localhost is used by default.
# redis_url: "redis://localhost:6379/1"
# Use the same channel name for WebSocket server, e.g.:
# $ anycable-go --redis_channel="__anycable__"
# redis_channel: "__anycable__"

development:
<<: *default
# WebSocket endpoint of your AnyCable server for clients to connect to
# Make sure you have the `action_cable_meta_tag` in your HTML layout
# to propogate this value to the client app
websocket_url: "ws://localhost:8080/cable"

test:
<<: *default

production:
<<: *default
websocket_url: ~
2 changes: 2 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,7 @@ class Application < Rails::Application
port: backend.port,
protocol: backend.scheme
}

config.host_authorization = {exclude: ->(request) { request.path.start_with?("/up") }}
end
end
8 changes: 3 additions & 5 deletions config/cable.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
development:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://127.0.0.1:6379/1" } %>
adapter: <%= ENV.fetch("ACTION_CABLE_ADAPTER", "any_cable") %>
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: flyweight_development

test:
adapter: test

production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://127.0.0.1:6379/1" } %>
channel_prefix: flyweight_production
adapter: any_cable
34 changes: 19 additions & 15 deletions config/environments/cypress.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,28 @@
# In the development environment your application's code is reloaded any time
# it changes. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
config.enable_reloading = true

# Do not eager load code on boot.
config.eager_load = false

# Show full error reports.
config.consider_all_requests_local = true

# Enable server timing
# Enable server timing.
config.server_timing = true

# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
if Rails.root.join("tmp", "caching-dev.txt").exist?
config.cache_store = :memory_store
config.public_file_server.headers = {
"Cache-Control" => "public, max-age=#{2.days.to_i}"
}
else
config.action_controller.perform_caching = false
config.action_controller.perform_caching = false

config.cache_store = :null_store
end

# Store uploaded files on the local file system (see config/storage.yml for options).
# config.active_storage.service = :local
config.cache_store = :null_store

# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false

# Disable caching for Action Mailer templates even if Action Controller
# caching is enabled.
config.action_mailer.perform_caching = false

config.action_mailer.delivery_method = :test
Expand All @@ -57,14 +49,26 @@
# Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true

# Highlight code that enqueued background job in logs.
config.active_job.verbose_enqueue_logs = true

# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true

# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true
config.action_view.annotate_rendered_view_with_filenames = true

# Uncomment if you wish to allow Action Cable access from any origin.
# config.action_cable.disable_request_forgery_protection = true

# Raise error when a before_action's only/except options reference missing actions.
config.action_controller.raise_on_missing_callback_actions = true

# Apply autocorrection by RuboCop to files generated by `bin/rails generate`.
config.generators.apply_rubocop_autocorrect_after_generate!

# Mount Action Cable outside main process or domain.
config.action_cable.mount_path = nil

config.good_job.poll_interval = 1
end
3 changes: 3 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,7 @@

# Apply autocorrection by RuboCop to files generated by `bin/rails generate`.
config.generators.apply_rubocop_autocorrect_after_generate!

# Mount Action Cable outside main process or domain.
config.action_cable.mount_path = nil
end
2 changes: 1 addition & 1 deletion config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
# config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX

# Mount Action Cable outside main process or domain.
# config.action_cable.mount_path = nil
config.action_cable.mount_path = nil
# config.action_cable.url = "wss://example.com/cable"
# config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]

Expand Down
4 changes: 2 additions & 2 deletions config/initializers/content_security_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# - connect-src 'https://sessions.bugsnag.com'
#
# Vue.js in development requires:
# - connect-src 'ws://127.0.0.1:3035' 'http://127.0.0.1:3035'
# - connect-src 'ws://localhost:3035' 'http://localhost:3035'

extra_image_sources = %w[]
extra_script_sources = []
Expand All @@ -20,7 +20,7 @@

if Rails.env.development? || Rails.env.cypress?
extra_script_sources << :unsafe_eval << :unsafe_inline
# extra_connect_sources << "ws://127.0.0.1:3035" << "http://127.0.0.1:3035"
# extra_connect_sources << "ws://localhost:3035" << "http://localhost:3035"
end

Rails.application.configure do
Expand Down
Loading

0 comments on commit 33ad3f0

Please sign in to comment.