Skip to content

Commit

Permalink
Migrate to Kamal 2
Browse files Browse the repository at this point in the history
  • Loading branch information
vladyio authored Oct 1, 2024
2 parents f1003ec + e0d671e commit 84d4f7f
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 92 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.

# Ignore git directory.
/.git/
shot.png

# Ignore bundler config.
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
config/deploy.yml
config/deploy.*.yml

# Kamal 2 secrets
.kamal/secrets*
!.kamal/secrets.sample

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
Expand Down
3 changes: 0 additions & 3 deletions .env.sample → .kamal/secrets.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
REGISTRY_PASSWORD=YOU_REGISTRY_TOKEN
SECRET_KEY_BASE=MASTER_KEY
MAX_FILESIZE=1G
RETRIES=5
PATHS=public/dl
9 changes: 6 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ FROM base

# Install packages needed for deployment
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl ffmpeg cron && \
apt-get install --no-install-recommends -y curl ffmpeg cron git && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Copy built artifacts: gems, application
Expand All @@ -54,8 +54,9 @@ ADD https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux /usr/
RUN chmod 755 /usr/local/bin/yt-dlp

# Run and own only the runtime files as a non-root user for security
RUN useradd rails --create-home --shell /bin/bash && \
chown -R rails:rails log tmp public/dl
RUN useradd rails --home /home/rails --shell /bin/bash && \
mkdir -p /home/rails && \
chown -R rails:rails log tmp public/dl /home/rails

RUN chmod u+s /usr/sbin/cron

Expand All @@ -64,6 +65,8 @@ RUN chmod -R 700 /rails/public/dl

USER rails:rails

RUN git config --global --add safe.directory /rails

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

Expand Down
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,36 @@
<hr>

<div align="center">
💎 Ruby 3.3 · 🛤 Rails 7.2 · ⚡️ Stimulus · 🅺 Kamal
💎 Ruby 3.3 · 🛤 Rails 7.2 · ⚡️ Stimulus · 🅺 Kamal 2
</div>

<hr>

# Deploy

0. Clone the repository
1. Install `kamal` (see [Kamal docs](https://kamal-deploy.org/docs/installation/)):
> [!NOTE]
> For Kamal v1 with Traefik, see [legacy v0.1.7.1](https://github.com/vladyio/quicktube/tree/v0.1.7.1).
1. Clone the repository
2. Install `kamal` (see [Kamal docs](https://kamal-deploy.org/docs/installation/)):

`gem install kamal`
2. Create a `deploy/config.yml` file from sample:
3. Create a `config/deploy.yml` file from sample:

`cp deploy/config.yml.sample deploy/config.yml`
3. Change `deploy/config.yml` to suit your needs
4. Create a `.env` file from sample:
`cp config/deploy.yml.sample config/deploy.yml`
4. Set values in `config/deploy.yml` to match your setup
5. Create a `.kamal/secrets` file from sample:

`cp .env.sample .env`
5. Change `.env` to suit your needs
6. Prepare server(s) - everything from copying an SSH key to setting up UFW, users and permissions:
`cp .kamal/secrets.sample .kamal/secrets`
6. Set values in `.kamal/secrets` to match your setup
7. Prepare server(s) - everything from copying an SSH key to setting up UFW, users and permissions:

```
./bin/prepare_server
```
7. Finally, deploy:
8. Finally, deploy:

```
kamal env push
kamal accessory boot redis
kamal deploy
```
Expand All @@ -47,13 +49,12 @@

It's possible to prepare & deploy a custom environment too.

Make sure you have a `config/deploy.[environment].yml` and `.env.[environment]` files.
Make sure you have a `config/deploy.[environment].yml` and `.kamal/secrets.[environment]` files.

For example, for a `staging` environment:

./bin/prepare_server staging

kamal env push -d staging
kamal accessory boot redis -d staging
kamal deploy -d staging.

17 changes: 13 additions & 4 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,20 @@
</main>

<footer class="text-center mt-[-50px]">
<a href="https://github.com/vladyio/quicktube"
class="text-blue-400 border-b-2 border-blue-400"
<section>
<a href="https://github.com/vladyio/quicktube"
class="text-blue-400 border-b-2 border-blue-400"
_target="blank">
GitHub
</a>
GitHub
</a>
</section>

<section class="mt-2">
<sup class="text-blue-400">
<%= %x[git tag --list | sort | tail -n 1] %>
(<%= %x[git branch --show-current].strip %>)
</sup>
</section>
</footer>
</body>
</html>
13 changes: 1 addition & 12 deletions bin/prepare_server
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,6 @@ prepare_storage = <<~EOF
chown 1000:1000 /rails/public/dl
EOF

# Prepare Let's Encrypt
prepare_letsencrypt = <<~EOF
mkdir -p /etc/letsencrypt;
touch /etc/letsencrypt/acme.json;
chmod 600 /etc/letsencrypt/acme.json
EOF

# Add non-root user
add_user = <<~EOF
useradd --create-home #{user_name};
Expand All @@ -56,12 +49,11 @@ add_user = <<~EOF
visudo -c -f /etc/sudoers.d/#{user_name}
EOF

# Install Docker and add the private network
# Install Docker
install_docker = <<~EOF
docker -v || curl -fsSL https://get.docker.com | sh;
systemctl enable docker;
systemctl start docker;
docker network create quicktube;
groupadd docker;
usermod -aG docker #{user_name}
EOF
Expand Down Expand Up @@ -127,8 +119,6 @@ end
ssh.exec!(install_essentials)
puts_info "Preparing storage for disk service..."
ssh.exec!(prepare_storage)
puts_info "Preparing Let's Encrypt..."
ssh.exec!(prepare_letsencrypt)
puts_info "Adding user with sudo privileges..."
ssh.exec!(add_user)
puts_info "Installing and configuring Docker..."
Expand All @@ -148,7 +138,6 @@ puts_info " ssh #{user_name}@#{hosts.first} #{'-p ' + ssh_port.to_s if ssh_port

puts "To deploy, run:"
puts <<-EOF
kamal env push #{'-d ' + environment if environment}
kamal accessory boot redis #{'-d ' + environment if environment}
kamal deploy #{'-d ' + environment if environment}
EOF
69 changes: 16 additions & 53 deletions config/deploy.yml.sample
Original file line number Diff line number Diff line change
@@ -1,37 +1,33 @@
service: quicktube

image: username/quicktube

servers:
web:
options:
network: quicktube
hosts:
- HOST_IP
labels:
traefik.http.routers.quicktube-web.rule: "Host(`domain.com`) || Host(`www.domain.com`)"
traefik.http.routers.quicktube-web-secure.entrypoints: websecure
traefik.http.routers.quicktube-web-secure.rule: "Host(`domain.com`) || Host(`www.domain.com`)"
traefik.http.routers.quicktube-web-secure.tls: true
traefik.http.routers.quicktube-web-secure.tls.certresolver: letsencrypt

traefik.http.routers.quicktube-dl.rule: "Host(`domain.com`) && PathPrefix(`/dl/`)"
traefik.http.routers.quicktube-dl.entrypoints: websecure
traefik.http.routers.quicktube-dl.middlewares: quicktube-dl-middleware
traefik.http.middlewares.quicktube-dl-middleware.headers.accesscontrolalloworiginlist: "*"
traefik.http.middlewares.quicktube-dl-middleware.headers.accesscontrolallowmethods: "GET,OPTIONS,HEAD"
job:
hosts:
- HOST_IP
cmd: bundle exec sidekiq
options:
network: quicktube
cron:
hosts:
- HOST_IP
cmd: bash -c "cat config/crontab | crontab - && cron -f"
cmd: bash -c "cat config/crontab | crontab - && declare -p | grep -Ev '\b(BASHOPTS|BASH_VERSINFO|EUID|PPID|SHELLOPTS|UID)\b' >> /rails/tmp/.cron.env && cron -f"

builder:
arch: amd64

proxy:
ssl: true
host: example.com,www.example.com
app_port: 3000

volumes:
- "/rails/public/dl:/rails/public/dl"

asset_path: /rails/public/assets

registry:
server: ghcr.io
username: username
Expand All @@ -41,44 +37,15 @@ registry:
env:
clear:
REDIS_URL: redis://quicktube-redis:6379/0
MAX_FILESIZE: 1G
RETRIES: 5
PATHS: public/dl
secret:
- SECRET_KEY_BASE

ssh:
user: admin

traefik:
image: traefik:3.0.3
options:
publish:
- "443:443"
volume:
- "/etc/letsencrypt:/letsencrypt"
network: quicktube
args:
log: true
log.level: ERROR
api.dashboard: true
api.insecure: false
entryPoints.web.address: ":80"
entryPoints.websecure.address: ":443"
certificatesResolvers.letsencrypt.acme.email: "[email protected]"
certificatesResolvers.letsencrypt.acme.storage: "/letsencrypt/acme.json"
certificatesResolvers.letsencrypt.acme.httpchallenge: true
certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web
labels:
traefik.http.routers.dashboard.rule: "Host(`traefik.domain.com`) || Host(`www.traefik.domain.com`)"
traefik.http.routers.dashboard.service: api@internal
traefik.http.routers.dashboard.middlewares: redirect-to-https, auth
traefik.http.routers.dashboard.tls: true
traefik.http.middlewares.auth.basicauth.users: [Run `htpasswd -nb username password` and copy here]
traefik.http.middlewares.redirect-to-https.redirectscheme.scheme: https
traefik.http.routers.catchall.rule: hostregexp(`{host:.+}`)
traefik.http.routers.catchall.service: noop@internal
traefik.http.routers.catchall.entrypoints: websecure
traefik.http.routers.catchall.middlewares: redirect-to-https
traefik.http.routers.catchall.tls: true

accessories:
redis:
roles:
Expand All @@ -87,7 +54,3 @@ accessories:
image: redis:7.2.5-alpine
directories:
- data:/data
options:
network: quicktube

asset_path: /rails/public/assets
4 changes: 2 additions & 2 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@

# Assume all access to the app is happening through a SSL-terminating reverse proxy.
# Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies.
# config.assume_ssl = true
config.assume_ssl = true

# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true

# Skip http-to-https redirect for the default health check endpoint.
# config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }

# Log to STDOUT by default
config.logger = ActiveSupport::Logger.new(STDOUT)
Expand Down

0 comments on commit 84d4f7f

Please sign in to comment.