How to conect to a dockerized Sinatra app - ruby

I'm trying to dockerize a simple Sinatra application, but when Docker is started I can't access the API through the exposed port 4567.
This is my Dockerfile:
FROM ruby:2.7
RUN apt-get update -qq && apt-get install -y build-essential
RUN mkdir /app
WORKDIR /app
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
RUN bundle install
COPY . /app
EXPOSE 4567
CMD [ "bundle", "exec", "ruby", "main.rb", "-p", "4567" ]
then:
docker build -t my-application .
docker run -p 4567:4567 my-application
when I visit localhost:4567:
enter image description here
but if I access the container and make a curl request, it works:
docker exec -it bdcbfcaa57aa bash
enter image description here
I don't see what I'm doing wrong.

It is most likely that your Sinatra application listens on 127.0.0.1. You need to bind it to 0.0.0.0 instead.
In classic Sinatra, it's done with:
require 'sinatra'
set :port, 4567
set :bind, '0.0.0.0'
# ... rest of the app
I am not sure what is in your main.rb, but you would probably want to add support for --host 0.0.0.0 or --bind 0.0.0.0.
Here is a fully functional example:
Dockerfile:
FROM dannyben/alpine-ruby
WORKDIR /app
COPY . .
RUN gem install puma sinatra
EXPOSE 3000
CMD ruby server.rb
server.rb:
require 'sinatra'
set :port, 3000
set :bind, '0.0.0.0'
get '/' do
"we are the champions"
end
Then run:
$ docker build -t temp .
$ docker run --rm -it -p 3000:3000 temp

Related

Keep docker container running in case of error

I have following docker file
FROM ruby:latest
# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1
WORKDIR /usr/src/app/
COPY Gemfile Gemfile.lock ./
RUN bundle install
ADD . /usr/src/app/
EXPOSE 3333
CMD ["ruby", "/usr/src/app/helloworld.rb"]
When I run image
docker run -t -d hello-world-ruby
Sinatra throws exception (which is OK), and container exits.
How can I keep it running so I can ssh into container and debug what's happening?
A trick you can use is to start the application with a script.
The following will work:
FROM ruby:latest
# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1
WORKDIR /usr/src/app/
COPY Gemfile Gemfile.lock ./
RUN bundle install
ADD . /usr/src/app/
EXPOSE 3333
CMD [/usr/src/app/startup.sh]
and in startup.sh, do:
#!/bin/bash
ruby /usr/src/app/helloworld.rb &
sleep infinity
# or if you're on a version of linux that doesn't support bash and sleep infinity
while true; do sleep 86400; done
chmod 755 and you should be set.
For debugging puproposes it is not needed to keep the container with a failing command alive, with loops or otherwise.
To debug such issues just spawn a new container from the image with entrypoint/command as bash.
docker run -it --name helloruby hello-world-ruby bash
OR
docker run -it --name helloruby --entrypoint bash hello-world-ruby
This will give you shell inside the container where you can run/debug the ruby app
ruby /usr/src/app/helloworld.rb

How to run cronjobs and rackup

My problem is when run cron and rackup service for ruby sinatra in docker.
file cronjobs
* * * * * cd /app && rake parser >> cron.log 2>&1
file Dockerfile
RUN apk update && apk upgrade
RUN apk add --update build-base \
mariadb-dev bash dcron
RUN gem install bundler
WORKDIR /app
COPY Gemfile .
RUN bundle install && bundle clean
COPY . /app
COPY cronjobs /etc/crontabs/root
EXPOSE 80
CMD crond -f && rackup --host 0.0.0.0 -p 80
When run docker only one service is functional
Docker container is running while main process inside it is running. So if you want to run two services inside docker container, one of them has to be run in a background mode.
So, CMD layer should be the following:
CMD ( crond -f & ) && rackup --host 0.0.0.0 -p 80

Configure Dockerfile to Run Cron Tasks using Whenver and Rake

I want to create a container using Docker which is will be responsible for starting recurrent rake tasks based on the Whenever gem's configuration. I have a plain ruby project (without rails/sinatra) with the following structure:
Gemfile:
source 'https://rubygems.org'
gem 'rake', '~> 12.3', '>= 12.3.1'
gem 'whenever', '~> 0.9.7', require: false
group :development, :test do
gem 'byebug', '~> 10.0', '>= 10.0.2'
end
group :test do
gem 'rspec', '~> 3.5'
end
config/schedule.rb: (whenever's configuration)
ENV.each { |k, v| env(k, v) }
every 1.minutes do
rake 'hello:start'
end
lib/tasks/hello.rb: (rake configuration)
namespace :hello do
desc 'This is a sample'
task :start do
puts 'start something!'
end
end
Dockerfile:
FROM ruby:2.5.3-alpine3.8
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories && \
apk update && apk upgrade && \
apk add build-base bash dcron && \
apk upgrade --available && \
rm -rf /var/cache/apk/* && \
mkdir /usr/app
WORKDIR /usr/app
COPY Gemfile* /usr/app/
RUN bundle install
COPY . /usr/app
RUN bundle exec whenever --update-crontab
CMD ['sh', '-c', 'crond && gulp']
I've used the following resources to get at the this point
How to run a cron job inside a docker container
https://github.com/renskiy/cron-docker-image/blob/master/alpine/Dockerfile
https://stackoverflow.com/a/43622984/5171758 <- very close to I want but no success
If I call my rake task using command line, I get the result I want.
$ rake 'hello:start'
start something!
However, I can't figure out how to make it work using Docker. The container is build but no log is written, no output is shown, nothing happens. Can someone help me showing what I'm doing wrong?
building commands
docker build -t gsc:0.0.1 .
docker container run -a stdin -a stdout -i --net host -t gsc:0.0.1 /bin/bash
Thanks all. Cheers
This is the solution to the problem I listed above. I had some issues at the Dockerfile and schedule.rb. This is what I had to change to make it work correctly.
Dockerfile
wrong echo call
wrong bundle command
change ENTRYPOINT instead of CMD
FROM ruby:2.5.3-alpine3.8
RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/main && \
apk update && apk upgrade && \
apk add build-base bash dcron && \
apk upgrade --available && \
rm -rf /var/cache/apk/* && \
mkdir /usr/app
WORKDIR /usr/app
COPY Gemfile* /usr/app/
RUN bundle install
COPY . /usr/app
RUN bundle exec whenever -c && bundle exec whenever --update-crontab && touch ./log/cron.log
ENTRYPOINT crond && tail -f ./log/cron.log
config/schedule.rb
no need to ENV.each
every 1.minutes do
rake 'hello:start'
end
UPDATE
I've created a GitHub repository and a Docker Hub repository to share with the community this progress.

Can't run docker - repository does not exist

I have created a Dockerfile in my file structure, built a docker repository, and tried to run it, but I keep getting the following error:
Error response from daemon: repository not found, does not exist, or no pull access.
I'm pretty new to docker, so what could possibly be wrong here?
Commands I run:
docker build -t repoName .
docker run -d -p 8080:80 repoName
My Dockerfile:
FROM nginx:1.10.3
RUN mkdir /app
WORKDIR /app
# Setup enviromnet
RUN apt-get update
RUN apt-get install -y curl tar
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
RUN apt-get install -y nodejs
# Do some building and copying
EXPOSE 80
CMD ["/bin/sh", "/app/run-dockerized.sh"]

Running Ruby Sinatra inside a Docker container not able to connect (via Mac host) or find commands (in different scenario)?

I've tried two forms of Dockerfile to get a simple Ruby/Sinatra app running, and in both scenarios it fails for different reasons (I'll explain both in a moment).
Effectively I want to access the Sinatra web server from my host (Mac OS X using Boot2Docker).
The app structure is:
.
├── Dockerfile
├── Gemfile
├── app.rb
├── config.ru
The contents of the files are:
Dockerfile
Version 1...
FROM ruby
RUN mkdir -p /app
WORKDIR /app
COPY Gemfile /app/
RUN bundle install --quiet
COPY . /app
EXPOSE 5000
ENTRYPOINT ["bash"]
CMD ["bundle", "exec", "rackup", "-p", "5000"]
Version 2...
FROM ubuntu:latest
RUN apt-get -qq update
RUN apt-get -qqy install ruby ruby-dev
RUN apt-get -qqy install libreadline-dev libssl-dev zlib1g-dev build-essential bison openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev
RUN gem install bundler
RUN mkdir -p /app
WORKDIR /app
COPY Gemfile /app/
RUN bundle install --quiet
COPY . /app
EXPOSE 5000
CMD ["bundle", "exec", "rackup", "-p", "5000"]
Gemfile
source "https://rubygems.org/"
gem "puma"
gem "sinatra"
app.rb
require "sinatra/base"
class App < Sinatra::Base
set :bind, "0.0.0.0"
get "/" do
"<p>hello world</p>"
end
end
config.ru
require "sinatra"
require "./app.rb"
run App
I build the docker image like so:
docker build --rm -t ruby_app .
I run the container like so:
docker run -d -p 7080:5000 ruby_app
I then try to verify I can connect to the running service (on my Mac using Boot2Docker) like so:
curl $(boot2docker ip):7080
With version 1 of the Dockerfile I get the following error before being able to run the curl command:
/usr/local/bundle/bin/rackup: line 9: require: command not found
/usr/local/bundle/bin/rackup: rackup: line 10: syntax error near unexpected token `('
/usr/local/bundle/bin/rackup: rackup: line 10: `ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../../../../app/Gemfile",'
With version 2 of the Dockerfile it seems to run the rack server fine from inside the container but I'm unable to connect via the host environment and so when running the curl command I get the error:
curl: (7) Failed to connect to 192.168.59.103 port 7080: Connection refused
Does anyone have any idea as to what I'm missing? Seems it shouldn't be this hard to get a very simple Ruby/Sinatra app running inside a Docker container from which I can access via my host (Mac OS X via Boot2Docker).
Change the dockerfile to use this instead:
["bundle", "exec", "rackup", "--host", "0.0.0.0", "-p", "5000"]

Resources