Attach an iex shell to a running elixir application - heroku

I have an elixir (mix) application running on heroku
I'm having issues attaching a remote iex shell to this application
The application is launched via this command :
web: MIX_ENV=prod elixir --sname server -S mix run --no-halt
I have no trouble attaching a shell locally
MIX_ENV=prod elixir --sname server -S mix run --no-halt
iex --sname console --remsh server#mru2
However, when trying it on heroku I'm having the following issue :
heroku run "iex --sname console --remsh server#41959264-bef2-4d2e-b5de-6dcf618efa44"
Running `iex --sname console --remsh server#41959264-bef2-4d2e-b5de-6dcf618efa44` attached to terminal... up, run.4421
Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Could not contact remote node server#41959264-bef2-4d2e-b5de-6dcf618efa44, reason: :nodedown. Aborting...
It seems like the instance launched by heroku run cannot connect to the one running the server. I tried enforcing a common cookie, but to no avail.
What am I missing?

I'm almost positive that nodes running on Heroku dynos aren't permitted to communicate with each other. But as long as the cookie is shared between the two nodes and you are connecting to the right fully-qualified name, then the steps you took above are correct.

Related

iex: run compiled elixir binary in iex console

I have binary file compiled using mix release: bin/app.
How can I run it using iex so that I will be attached to process console and will be able to call functions from bin/app?
There are two ways:
bin/foo start_iex
connect to the running remote from iex (for this the application should have been started named.)
Assuming the application was run as foo#192.168.1.42, and local host is 192.168.1.10, this would open a remote shell:
iex --name remote#192.168.1.10 \
--remsh foo#192.168.1.42 \
--cookie COOKIE

How to look Gunicorn logs when it running?

I deployed a Flask application to a VPS, and using Gunicorn as a web server.
And I running the Gunicorn server using this command:
gunicorn --bind=0.0.0.0 run:app --access-logfile '-'
With the command I can see the log running. But after I closed my terminal session, I want to see the running logs again.
In Heroku I can use heroku logs -t to do that, any similar way to see it on Gunicorn..?
You need to set up the supervisor. Supervisor keeps your server running mode and saves your log. setup the supervisor file below and then you can see the logs:
[program:your_project_name]
command=/home/your_virualenv/bin/gunicorn --log-level debug
run_apiengine:main_app --bind 0.0.0.0:5007 --workers 2 --worker-class gevent
directory=your_project_directory
stdout_logfile= your_log_folder_path/supervisor_stdout.log
stderr_logfile= your_log_folder_path/supervisor_stderr.log
user=your_user
autostart=true
PYTHONPATH="$PYTHONPATH:your_python_path";OAUTHLIB_INSECURE_TRANSPORT='1';

How can you run an iex session and a phoenix server attached to the same database and mix environment in production?

When you run a server like MIX_ENV=prod mix phx.server and then on another screen attached to the same server try to run a mix-environment attached iex session like iex -S mix then you get an error and shutdown with a complaint like Failed to start Ranch listener VukWeb.Endpoint.HTTP in :ranch_tcp:listen([port: 4000]) for reason :eaddrinuse (address already in use)
Is there a way to run an iex session while a server is running in a separate screen attached to the same database? I'm confused as to why iex -S mix is even trying to connect to an external port, as that iex session should have no server running nor require an external port simply for loading the mix environment?
I'm aware you can run the server with an iex session like MIX_ENV=prod iex -S mix phx.server, but my understanding is that is neither ideal for performance nor is it nice to have your iex session interrupted by streams of user logs as requests are processed (which is what we're doing right now). I also tried switching up the port like MIX_ENV=prod PORT=4040 iex -S mix but the flag seems to go ignored, as the complaint comes back the same with a reference to port 4000. I'm wonder if maybe there's some hardcoding that's causing the environment variable to be ignored, and if just undoing this hardcoding and switching it to a different port like this is the right approach, even if the port goes unused by the non-server mix environment.
If anyone has a tip on how you can get both a iex session and serving running—or has a different suggestion for workflow that makes such a desire unnecessary—would love to hear!
Thanks
Mix has a perfect very detailed documentation.
Mix.Tasks.Run is a default task. iex -S -mix is essentially equivalent to iex -S mix run.
The latter starts your application, which [wild reasonable guess] in turn starts cowboy as a dependency. Hence the error.
iex -S mix run --no-start
is what you are looking for. This task accepts several arguments, other are listed on the help page I linked.

Elixir phoenix debugging leads to interactive shell instead of pry

I've been trying to debug a phoenix application.
To do so I used the following instructions:
- To set a break point: require IEx; IEx.pry
- To start a debugging server: iex -S mix phx.server
The problem comes when starting the server, the above instruction leads me to the elixir interactive shell (iex(1)>) and does not allow the server to run, from here if I execute the code manually it stops in the prys but I was hopping to have the server running and stop whenever a request hit the pry. Is there any solution?
I am currently using earlang 1.20, elixir 1.5 and phoenix 1.3
It is common behavior that the iex -S mix phx.server command opens an IEx shell. It runs the web server on the port configured for the Phoenix endpoint within the respective MIX_ENV simultaneously to that. For each IEx.pry() breakpoint it executes, it should prompt on the command line whether you want to open an IEx shell there.
Check which MIX_ENV your server is running in, e.g. by typing the following in the IEx shell:
Mix.env
And then find the configuration in the files within the config/ directory. The line should look like this:
config :nameofyourapp, Web.Endpoint,
http: [port: 4000],
Maybe something other than 4000 is configured there? Or maybe some other application, or even a previously started instance of your web application already listens on that port and therefore prohibits the debug server from binding on that port? In that case, shutdown other running mix phx.server processes.

Hooking up into running heroku phoenix application

Previous night I was tinkering with Elixir running code on my both machines at home, but when I woke up, I asked myself Can I actually do the same using heroku run command?
I think theoretically it should be entirely possible if setup properly. Obviously heroku run iex --sname name executes and gives me access to shell (without functioning backspace which is irritating) but i haven't accessed my app yet.
Each time I executed the command it gave me different machine. I guess it's how Heroku achieve sandbox. I also was trying to find a way to determine address of my app's machine but haven't got any luck yet.
Can I actually connect with the dyno running the code to evaluate expressions on it like you would do iex -S mix phoenix.server locally ?
Unfortunately it's not possible.
To interconnect Erlang VM nodes you'd need EPMD port (4369) to be open.
Heroku doesn't allow opening custom ports so it's not possible.
In case You'd want to establish a connection between your Phoenix server and Elixir node You'd have to:
Two nodes on the same machine:
Start Phoenix using iex --name phoenix#127.0.0.1 -S mix phoenix.server
Start iex --name other_node#127.0.0.1
Establish a connection using Node.ping from other_node:
iex(other_node#127.0.0.1)1> Node.ping(:'phoenix#127.0.0.1')
(should return :pong not :pang)
Two nodes on different machines
Start Phoenix using some external address
iex --name phoenix#195.20.2.2 --cookie someword -S mix phoenix.server
Start second node
iex --name other_node#195.20.2.10 --cookie someword
Establish a connection using Node.ping from other_node:
iex(other_node#195.20.2.10)1> Node.ping(:'phoenix#195.20.2.2')
(should return :pong not :pang)
Both nodes should contact each other on the addresses they usually see each other on the network. (Full external IP when different networks, 192.168.X.X when in the same local network, 127.0.0.1 when on the same machine)
If they're on different machines they also must have set the same cookie value, because by default it takes automatically generated cookie in your home directory. You can check it out by running:
cat ~/.erlang.cookie
What's last you've got to make sure that your EPMD port 4369 is open, because Erlang VM uses it for internode data exchange.
As a sidenote if you will leave it open make sure to make your cookie as private as possible, because if someone knows it, he can have absolute power over your machine.
When you execute heroku run it will start a new one-off dyno which is a temporary instance that is deprovisioned when you finish the heroku run session. This dyno is not a web dyno and cannot receive inbound HTTP requests through Heroku's routing layer.
From the docs:
One-off dynos can never receive HTTP traffic, since the routers only route traffic to dynos named web.N.
https://devcenter.heroku.com/articles/one-off-dynos#formation-dynos-vs-one-off-dynos
If you want your phoenix application to receive HTTP requests you will have to set it up to run on a web dyno.
It has been a while since you've asked the question, but someone might find this answer valuable, though.
As of 2021 Heroku allows forwarding multiple ports, which allows to remsh into a running ErlangVM node. It depends on how you deploy your application, but in general, you will need to:
Give your node a name and a cookie (i.e. --name "myapp#127.0.0.1" --cookie "secret")
Tell exactly which port a node should bind to, so you know which pot to forward (i.e. --erl "-kernel inet_dist_listen_min 9000 -kernel inet_dist_listen_max 9000")
Forward EPMD and Node ports by running heroku ps:forward 9001:4369,9000
Remsh into your node: ERL_EPMD_PORT=9001 iex --cookie "secret" --name console#127.0.0.1 --remsh "myapp#127.0.0.1"
Eventually you should start your server with something like this (if you are still using Mix tool): MIX_ENV=prod elixir --name "myapp#127.0.0.1" --cookie "secret" --erl "-kernel inet_dist_listen_min 9000 -kernel inet_dist_listen_max 9000" -S mix phx.server --no-halt
If you are using Releases, most of the setup has already been done for you by the Elixir team.
To verify that EPMD port has been forwarded correctly, try running epmd -port 9001 -names. The output should be:
epmd: up and running on port 4369 with data:
name myapp#127.0.0.1 at port 9000
You may follow my notes on how I do it for Dockerized releases (there is a bit more hustle): https://paveltyk.medium.com/elixir-remote-shell-to-a-dockerized-release-on-heroku-cc6b1196c6ad

Resources