Webservice does not work after being packaged into a Docker image - ruby

I write a simple webservice in ruby with sinatra. If I run ruby app.rb it runs on localhost:4567. I write a Dockerfile to make an image and expose port 4567.
However, when I run the docker image the webservice runs but if I try to connect (with curl and browser) to port 4567, it says Connection reset by peer.
Anyone has any suggestion? Because I do not know what to check in this case. I have tried some vague things but still..
The webservice runs normally outside docker.
EDIT 1:
I have pushed the image to eivor/ruby. If you run it and go to browser to check it would say connection reset. Yes I tried docker run -p 4567:4567 eivor/ruby as said before posting the question.
EDIT 2: Here is the app.rb
require 'sinatra'
require 'referal' # this is the gem that calculate reward points to users
require 'json'
require 'byebug'
get '/' do
'hello, world!'
end
# inside docker image, even get / returns connection reset by peer
# not to mention post data to it
post '/' do
data = JSON.parse(request.body.read)
input = []
data.each do | key, value |
input << value
end
invs, users = input.reduce([[],[]]) do | results, instruction |
results = classify(instruction, results[0], results[1])
results
end
res = export(users)
# byebug
puts res
end
post '/text' do
#data = request.body.readlines
#byebug
#processed = #data.map{ |s| process(s) }
#invs, #users = #processed.reduce([[],[]]) do | results, instruction |
results = classify(instruction, results[0], results[1])
results
end
#jsn = export(#users)
puts #jsn
end
Here is the Dockerfile, i build a lightweight ruby from alpine
FROM alpine:3.5
ENV BUILD_PACKAGES bash curl-dev ruby-dev build-base git libstdc++ tzdata ca-certificates
ENV RUBY_PACKAGES ruby>2.3 ruby-irb ruby-rake ruby-io-console ruby-bigdecimal ruby-json
RUN apk update && apk upgrade
RUN apk add $BUILD_PACKAGES && apk add $RUBY_PACKAGES
RUN apk add ruby-bundler>1.17
RUN echo 'gem: --no-document' > /etc/gemrc && rm -rf /var/cach/apk/*
RUN gem install bundler
RUN mkdir /usr/app
WORKDIR /usr/app
RUN git init
COPY . /usr/app
RUN bundle install
RUN bundle exec rake install
EXPOSE 4567
CMD ["ruby", "./app.rb"]
If I run outside docker with command ruby app.rb or bundle exec rerun app.rb it works normally. But using docker image, it does not. I run the command:
docker run -p 4567:4567 eivor/ruby
The server runs,
[2019-03-14 16:59:59] INFO WEBrick 1.3.1
[2019-03-14 16:59:59] INFO ruby 2.3.8 (2018-10-18) [x86_64-linux-musl]
== Sinatra (v2.0.5) has taken the stage on 4567 for development with backup from WEBrick
[2019-03-14 16:59:59] INFO WEBrick::HTTPServer#start: pid=1 port=4567
but when I try to access with browser or curl, it says connection reset by peer. If I try to post with curl, the data is actually sent but it does not respond, it hangs up on me instead.
curl -v localhost:4567 --data-binary #test/input
* Rebuilt URL to: localhost:4567/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 4567 (#0)
> POST / HTTP/1.1
> Host: localhost:4567
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 369
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 369 out of 369 bytes
* Recv failure: Connection reset by peer
* stopped the pause stream!
* Closing connection 0
curl: (56) Recv failure: Connection reset by peer

This issue is happening because Sinatra by default (development environment) listen on 127.0.0.1 which not works in case of external connection to container
:bind - server hostname or IP address
String specifying the hostname or IP address of the interface to listen on when the :run setting is enabled. The default value in the development environment is 'localhost' which means the server is only available from the local machine. In other environments the default is '0.0.0.0', which causes the server to listen on all available interfaces.
So if you will continue on running in development mode, you need to change it to 0.0.0.0, for example:
docker run -p 4567:4567 --name stack eivor/ruby bash -c 'ruby ./app.rb -o 0.0.0.0'
Which can be used in Dockerfile as:
CMD ["ruby", "./app.rb", "-o", "0.0.0.0"]
Or you can use the following within your script:
set :bind, '0.0.0.0'
Then from outside the container you can get the result:
curl -v localhost:4567
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 4567 (#0)
> GET / HTTP/1.1
> Host: localhost:4567
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html;charset=utf-8
< Content-Length: 13
< X-Xss-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< Server: WEBrick/1.3.1 (Ruby/2.3.8/2018-10-18)
< Date: Thu, 14 Mar 2019 17:17:20 GMT
< Connection: Keep-Alive
<
* Connection #0 to host localhost left intact
hello, world!
For more configuration check the following: CONFIGURING SETTINGS

Have a look at this article: https://docs.docker.com/config/containers/container-networking/
In few words - try to publish your exposed ports via -p argument.
For example:
$ docker run -it -p 4567 my-local-image

From the documentation:
Flag value Description
-p 8080:80 Map TCP port 80 in the container to port 8080 on the Docker host.
You need to map the TCP port 4567 in the container to a port on the Docker host. For example, to map it to port 8080:
$ docker run -it -p 8080:4567 image-goes-here

Related

Curl request error (52) from docker container but works on host

I am running a Linux (Debian based) docker container on a Windows host.
I have a device with an HTTP based API that I want to interface with from the container.
When I run the curl command to the devices API under WSL from my host machine, it responds correctly.
<username>#<hostname>:/mnt/c/Users/$ curl -H "Authorization: Basic YWRtaW46aGVybW9zYTA=" http://172.16.255.33/Set.cmd?CMD=GetPower -v
* Trying 172.16.255.33:80...
* TCP_NODELAY set
* Connected to 172.16.255.33 (172.16.255.33) port 80 (#0)
> GET /Set.cmd?CMD=GetPower HTTP/1.1
> Host: 172.16.255.33
> User-Agent: curl/7.68.0
> Accept: */*
> Authorization: Basic YWRtaW46aGVybW9zYTA=
>
* Mark bundle as not supporting multiuse
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Connection: close
<
<html>p61=1,p62=1,p63=0,p64=0</html>
* Closing connection 0
However, when I do the same curl command from my container, I get the error (52) Empty reply from server
root#9af1adf97ba7:/# curl -H "Authorization: Basic YWRtaW46aGVybW9zYTA=" http://172.16.255.33/Set.cmd?CMD=GetPower -v
* Trying 172.16.255.33:80...
* Connected to 172.16.255.33 (172.16.255.33) port 80 (#0)
> GET /Set.cmd?CMD=GetPower HTTP/1.1
> Host: 172.16.255.33
> User-Agent: curl/7.74.0
> Accept: */*
> Authorization: Basic YWRtaW46aGVybW9zYTA=
>
* Empty reply from server
* Connection #0 to host 172.16.255.33 left intact
curl: (52) Empty reply from server
In Wireshark, I see the device is setting the RST flag in its response when queried from the container. No such reset in the response when queried from my host machine.
I am starting my container using these arguments:
docker run -it -p 80:80 <image-name> bash
When I start to run the container on a Linux host using the same args, the curl request succeeds (as a test, I have to run this app on a Windows host).
I have tried both WSL2 based and Hyper-V based versions of Docker on Windows.
Under WSL2 container, I get no response from the device.
Under Hyper-V based container, I get the issue described above.
I've spent about a day Googling and trying various things, and I am out of ideas at this point.
Thanks.

Docker issue during gatling performance test

I have a spring boot application, and I run a performance test on it, using Gatling.
The issue is that after a few requests where everything works OK, the server returns connection refused and no other requests are working.
Gatling log looks like this:
---- Requests ------------------------------------------------------------------
> Global (OK=14 KO=1001 )
> POST /template (OK=13 KO=938 )
> PUT /feedback (OK=1 KO=63 )
---- Errors --------------------------------------------------------------------
> j.n.ConnectException: Connection refused: no further informati 577 (57,64%)
on
> j.i.IOException: Premature close 240 (23,98%)
> j.n.c.ClosedChannelException 184 (18,38%)
When I create a manual request using curl, returns:
$ curl https://localhost:8087
curl: (7) Failed to connect to localhost port 8087: Connection refused
If I connect to docker and do the request:
$ docker exec -it web /bin/bash
root#794f9e808f14:/# curl https://localhost:8443
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
SSL handshake failed, as expected, but this means that the server is up an running.
The port is mapped in docker:
$ docker port web
8443/tcp -> 0.0.0.0:8087
8443/tcp -> :::8087
After restart, all thing happen again.
I'm using docker on a WSL Ubuntu. Not sure if this matters too much. What can I do to make this connection more stable?

Hostname was NOT found in DNS cache for port 8080 but fine on port 80?

I am testing an API I have made using Springboot from my laptop (192.168.1.217:8080) and I am trying to get a cURL request via SSH from my Raspberry Pi.
Here is the error I am receiving when I try to send the request via port 8080 which it seems to not like:
pi#raspberrypi:~ $ curl -v 192.168.1.217:8080/api
* Hostname was NOT found in DNS cache
* Trying 192.168.1.217...
However cURL does work for the same IP but with port 80:
pi#raspberrypi:~ $ curl -v 192.168.1.217
* Rebuilt URL to: 192.168.1.217/
* Hostname was NOT found in DNS cache
* Trying 192.168.1.217...
* Connected to 192.168.1.217 (192.168.1.217) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.38.0
> Host: 192.168.1.217
> Accept: */*
>
< HTTP/1.1 302 Found
< Date: Thu, 30 Mar 2017 17:20:43 GMT
* Server Apache/2.4.23 (Win32) OpenSSL/1.0.2h PHP/5.5.38 is not blacklisted
< Server: Apache/2.4.23 (Win32) OpenSSL/1.0.2h PHP/5.5.38
< X-Powered-By: PHP/5.5.38
< Location: http://192.168.1.217/dashboard/
< Content-Length: 0
< Content-Type: text/html
<
* Connection #0 to host 192.168.1.217 left intact
pi#raspberrypi:~ $
I've tried looking around but to no avail... anybody have any suggestions as to why I cannot find my own hostname in the DNS cache?
Cheers
No, Hostname was NOT found in DNS cache is not the problem. You can clearly see on the next line, in both examples, that after saying that curl is trying to connect to 192.168.1.217. Your problem is that nothing is answering on port 8080 on that IP address (while an Apache server is answering on port 80 there).
If you're getting a long pause and then a timeout rather than a quick "Connection refused", you almost certainly need to open port 8080 in the local firewall on your server machine.
curl without a protocol prefix presumes HTTP port 80.
To use another port, where it does not make that assumption, all you need to do is change your command's URL to be like this:
curl -v http://192.168.1.217:8080/api
Here is a decent article on the subject: Using CURL For Testing Web Applications

CircleCI: Best way to verify if docker containers are responding via HTTP

here is an example of part of my circle.yml how I am currently verifying if my mongodb docker container is responding to an http request, so that I can verify if the response is ok and that the server is up.
test:
override:
# RUN DOCKER CONTAINERS
# MongoDB -------------
- docker run --name MongoDB -p 27018:27018 -d mongo:3.0 mongod --port 27018 --replSet "rs"; sleep 10
- curl --retry 10 --retry-delay 5 -v http://localhost:27018
with this curl it's working fine, sometimes.
And sometimes it gives me this error:
curl --retry 10 --retry-delay 5 -v http://localhost:27018
* About to connect() to localhost port 27018 (#0)
* Trying 127.0.0.1... Connection refused
* couldn't connect to host
* Closing connection #0
How can I improve this, to make it more reliable so my tests won't sometimes pass and sometimes fail, if I rebuild the same build?
note: this question was also asked on discuss.circleci.com

Apt-get: Only Basic auth using server hostname with https not working

I have a local web server which is hosting all my debian packages from another machine I am trying to do apt-get update/upgrade to fetch the Package index list and upgrade the machine using https but only basic authorization as my web server is configured to do only basic auth and I do not want to change that to certificate based auth.
apt-get update with https basic auth works fine (i.e the client is able to skip the cert based authentication) when I use IP address of the web server but as soon as I try to use hostname of the web server then it doesn't work I keep getting the error "gnutls_handshake() failed: A TLS warning alert has been received."
Config for IP scenario which works with basic auth without certs
APT Config under apt.conf.d with IP:
Debug::Acquire::https "true";
Acquire::https::10.2.20.1 {
Verify-Host "false";
Verify-Peer "false";
};
source.list.d with IP:
deb [arch=amd64] https://username:password#10.2.20.1:443/foo bar test
Debugs when it works
0% [Working]* About to connect() to 10.2.20.1 port 443 (#0)
* Trying 10.2.20.1... * connected
* found 164 certificates in /etc/ssl/certs/ca-certificates.crt
* server certificate verification SKIPPED
* Server auth using Basic with user 'username'
> GET /foo/dists/bar/Release.gpg HTTP/1.1
Authorization: Basic
Config for hostname scenario doesn't work with basic auth without certs
APT Config under apt.conf.d with IP:
Debug::Acquire::https "true";
Acquire::https::my-foo-test.com {
Verify-Host "false";
Verify-Peer "false";
};
source.list.d with IP:
deb [arch=amd64] https://username:password#my-foo-test.com:443/foo bar test
Debug with TLS warning when hostname is used
root#my:~# apt-get update
0% [Working]* About to connect() to my-foo-test.com port 443 (#0)
* Trying 10.2.20.1... * connected
* found 164 certificates in /etc/ssl/certs/ca-certificates.crt
* gnutls_handshake() failed: A TLS warning alert has been received.
* Closing connection #0
Ign https://my-foo-test.com repo Release.gpg
I have resolved IP to hostname locally on my machine where I am running apt-get update using /etc/hosts file
Entry from /etc/hosts file
10.2.20.1 my-foo-test.com
Event tried below way but didn't work, tried putting this into apt.conf.d/ that didn't work either
apt-get update -o Debug::Acquire::https=true -o Acquire::https::Verify-Host=false -o Acquire::https::Verify-Peer=false -o Dir::Etc::SourceList="/etc/apt/sources.list.d/mysource.list" update
Thanks for the help!

Resources