Dockerfile and Docker compose

This commit is contained in:
Christoph Walther
2024-07-16 14:03:13 +02:00
parent 843daf8a35
commit ca84202168
12 changed files with 393 additions and 12 deletions

57
Dockerfile Normal file
View File

@ -0,0 +1,57 @@
FROM ruby:3.3.3
# Variables for
WORKDIR /app
# Install nodejs
ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 20.15.1
RUN mkdir -p $NVM_DIR
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash \
&& . $NVM_DIR/nvm.sh \
&& nvm install ${NODE_VERSION} \
&& nvm alias default ${NODE_VERSION} \
&& nvm use default
ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
RUN corepack enable
# Install dependencies for Rails
COPY Gemfile Gemfile.lock ./
RUN bundle install
#COPY package.json yarn.lock ./
COPY . .
RUN yarn install
# Setup default configuration
COPY config/database.yml.docker config/database.yml
COPY config/secrets.yml.docker config/secrets.yml
COPY config/action_mailer.yml.example config/action_mailer.yml
COPY config/code_ocean.yml.docker config/code_ocean.yml
COPY config/content_security_policy.yml.example config/content_security_policy.yml
COPY config/docker.yml.erb.example config/docker.yml.erb
COPY config/mnemosyne.yml.example config/mnemosyne.yml
# get a secret from `rails secret`
RUN secret_key_base=$(bundle exec rails secret) \
&& sed -i "s/secret_key_base: CHANGE_ME/secret_key_base: $secret_key_base/" config/secrets.yml
# precompile
ENV RAILS_SERVE_STATIC_FILES=true
RUN bundle exec rake assets:precompile
RUN yarn run webpack
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 7000
# run on HOSTNAME:PORT
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]

View File

@ -0,0 +1,74 @@
default: &default
# The following legal URLs are displayed in the help modal of the application.
# legal:
# imprint_url: https://open.hpi.de/pages/imprint
# privacy_policy_url: https://open.hpi.de/pages/data-protection
# A public-facing host to be used for the render_file function of the SubmissionsController.
# User content will be served from this host. If not set, the default host is used (less secure!).
# render_host: codeocean.openhpiusercontent.de
flowr:
# When enabled, flowr can assist learners with related search results from
# StackOverflow.com regarding exceptions that occurred during code execution.
# The search is initiated through the learners' browser and displayed in the output pane.
enabled: false
# The number of search results to be displayed
answers_per_query: 3
codeharbor:
# When enabled, CodeHarbor is integrated in the teachers' view and allows importing
# and exporting exercises from CodeOcean using the ProFormA XML format to CodeHarbor.
enabled: false
# The root URL of CodeHarbor
url: https://codeharbor.openhpi.de
codeocean_events:
# When enabled, learner-specific events within the editor are stored and can be used
# as part of learning analytics. This setting enables the JavaScript event handlers.
enabled: false
prometheus_exporter:
# When enabled, a dedicated endpoint using the Prometheus format is offered and might
# be used by a Prometheus-compatible monitoring system. Exported metrics include absolute
# counters of all relations with specific support for Request-for-Comments.
enabled: false
runner_management:
# When enabled, CodeOcean delegates the handling and management of (containerized) runners
# to a dedicated runner management. Otherwise, code executions are performed locally using
# Docker and without pre-warming support (one container per execution).
enabled: true
# The strategy to use. Possible values are: poseidon, docker_container_pool
strategy: poseidon
# The root URL of the runner management to use (include any API prefix if required)
# If a hostname is specified and the target host is reachable via IPv6, the WebSocket
# connection might not use the IPv6-to-IPv4 fallback but rather fail unexpectedly.
url: http://127.0.0.1:7200/api/v1
# The root certificate authority to trust for TLS connections to the runner management (Poseidon only)
# ca_file: /example/certificates/ca.crt
# The authorization token for connections to the runner management (Poseidon only)
# If TLS support is not enabled, this token is transmitted in clear text!
# token: SECRET
# The maximum time in seconds a runner may idle at the runner management before it is removed.
# Each begin of an interaction with the runner resets this time. Thus, this value should
# be truly greater than any permitted execution time of an execution environment.
unused_runner_expiration_time: 180
development:
<<: *default
flowr:
enabled: true
codeharbor:
enabled: true
production:
<<: *default
prometheus_exporter:
enabled: true
test:
<<: *default

View File

@ -0,0 +1,19 @@
default: &default
adapter: postgresql
pool: 16
username: <%= ENV['POSTGRES_USER'] %>
password: <%= ENV['POSTGRES_PASSWORD'] %>
host: db
development:
<<: *default
database: codeocean_development
production:
<<: *default
database: codeocean_production
test:
<<: *default
database: codeocean_test

View File

@ -53,11 +53,11 @@ Rails.application.configure do
# Assume all access to the app is happening through a SSL-terminating reverse proxy. # 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. # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies.
config.assume_ssl = true config.assume_ssl = false
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true config.force_ssl = false
config.ssl_options = {hsts: {preload: true}} #config.ssl_options = {hsts: {preload: true}}
if ENV['RAILS_LOG_TO_STDOUT'].present? if ENV['RAILS_LOG_TO_STDOUT'].present?
# Log to STDOUT by default # Log to STDOUT by default
@ -85,7 +85,7 @@ Rails.application.configure do
# Ignore bad email addresses and do not raise email delivery errors. # Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false config.action_mailer.raise_delivery_errors = false
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found). # the I18n.default_locale when a translation cannot be found).

26
config/secrets.yml.docker Normal file
View File

@ -0,0 +1,26 @@
# Shared secrets are available across all environments.
# shared:
# api_key: a1B2c3D4e5F6
# Environmental secrets are only available for that specific environment.
default: &default
secret_key_base: CHANGE_ME
admin:
email: <%= ENV['ADMIN_MAIL'] %>
password: <%= ENV['ADMIN_PASSWORD'] %>
development:
<<: *default
test:
<<: *default
# Do not keep production secrets in the unencrypted secrets file.
# Instead, either read values from the environment.
# Or, use `bin/rails secrets:setup` to configure encrypted secrets
# and move the `production:` environment over there.
production:
<<: *default

View File

@ -6,17 +6,22 @@ require 'highline/import'
FactoryBot.create(:consumer) FactoryBot.create(:consumer)
# users # users
email = ask('Enter admin email: ') email = Rails.application.secrets.dig(:admin, :email) || ask('Enter admin email: ')
password = Rails.application.secrets.dig(:admin, :password)
passwords = ['password', 'password confirmation'].map do |attribute| if password.nil? || password.empty?
ask("Enter admin #{attribute}: ") {|question| question.echo = false } passwords = ['password', 'password confirmation'].map do |attribute|
ask("Enter admin #{attribute}: ") {|question| question.echo = false }
end
if passwords.uniq.length == 1
password = passwords.first
else
abort('Passwords do not match!')
end
end end
if passwords.uniq.length == 1 admin = FactoryBot.create(:admin, email:, name: 'Administrator', password: password, study_groups: StudyGroup.all)
admin = FactoryBot.create(:admin, email:, name: 'Administrator', password: passwords.first, study_groups: StudyGroup.all)
else
abort('Passwords do not match!')
end
# file types # file types
FileType.create_factories user: admin FileType.create_factories user: admin

54
docker-compose.yml Normal file
View File

@ -0,0 +1,54 @@
services:
db:
image: postgres:16
volumes:
- db_data:/var/lib/postgresql/data
environment:
POSTGRES_USER: codeocean_user
POSTGRES_PASSWORD: secure_password
POSTGRES_DB: codeocean_production
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/codeocean
- config:/codeocean/config
ports:
- "7123:7000"
depends_on:
- db
- prometheus
environment:
POSTGRES_USER: codeocean_user
POSTGRES_PASSWORD: secure_password
ADMIN_MAIL: support@htwkalender.de
ADMIN_PASSWORD: htwkalender
RAILS_ENV: production
PROMETHEUS_EXPORTER_HOST: prometheus
PROMETHEUS_EXPORTER_PORT: 9090
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
ports:
- "9090:9090"
nginx:
image: nginx:latest
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./nginx/certs:/etc/nginx/certs
ports:
- "443:443"
depends_on:
- app
restart: always
volumes:
db_data:
config:

16
entrypoint.sh Normal file
View File

@ -0,0 +1,16 @@
#!/bin/bash
set -e
# Überprüfen, ob die Datenbank eingerichtet werden muss
if [ -f /codeocean/tmp/pids/server.pid ]; then
rm /codeocean/tmp/pids/server.pid
fi
# Führen Sie db:setup nur aus, wenn es notwendig ist
bundle exec rake db:prepare
# Main process is the following command
echo "Starting the main process with the following command: $@"
# Starten Sie den Hauptprozess
exec "$@"

34
nginx/certs/codeocean.crt Normal file
View File

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

52
nginx/certs/codeocean.key Normal file
View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
pviII3pjCPh6v5cRhNJlPF+XqZhb1g==
-----END PRIVATE KEY-----

15
nginx/default.conf Normal file
View File

@ -0,0 +1,15 @@
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /etc/nginx/certs/codeocean.crt;
ssl_certificate_key /etc/nginx/certs/codeocean.key;
location / {
proxy_pass http://app:7000; # Ersetzen Sie 'frontend' durch den Namen Ihres Frontend-Services und '80' durch den Port, auf dem Ihr Frontend läuft
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

29
prometheus.yml Normal file
View File

@ -0,0 +1,29 @@
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus"
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ["localhost:9090"]