Skip to content

Commit da126b6

Browse files
authored
Add support for Fly.io deployments (#167)
1 parent e42c09d commit da126b6

File tree

7 files changed

+89
-19
lines changed

7 files changed

+89
-19
lines changed

guides/advanced/debugging.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,4 @@ You can also configure PeerConnection to use a specific port range by doing
162162

163163
Otherwise, the connection won't be established at all, or just in one direction.
164164

165-
Read more in our [Deploying tutorial](./deploying.md#allow-udp-traffic-in-your-firewall)!
165+
Read more in our [Deploying tutorial](../deploying/bare.md#allow-udp-traffic-in-your-firewall)!

guides/advanced/deploying.md renamed to guides/deploying/bare.md

+4-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# Deploying
1+
# Deploying on bare machines
22

33
Deploying WebRTC applications can be cumbersome.
4-
Here are a few details you should keep in mind when trying to push your project into production.
4+
Here are a few details you should keep in mind when trying to push your project into production on a bare machine.
55

66
## Allow UDP traffic in your firewall
77

@@ -37,12 +37,6 @@ docker run -p 50000-50010/udp myapp
3737
Keep in mind that exporting a lot of ports might take a lot of time or even cause the Docker daemon to timeout.
3838
That's why we recommend using host's network.
3939

40-
## Choose your cloud provider wisely
41-
42-
Many cloud providers do not offer good support for UDP traffic.
43-
In such cases, deploying a WebRTC-based application might be impossible.
44-
We recommend using bare machines that you can configure as you need.
45-
4640
## Enable HTTPS in your frontend
4741

4842
The server hosting your frontend site must have HTTPS enabled.
@@ -66,7 +60,7 @@ Read more [here](https://nginx.org/en/docs/http/websocket.html).
6660

6761
## Configure STUN servers
6862

69-
If you are deploying your application behind a NAT, you have to configure a STUN
63+
If you are deploying your application behind a NAT, you have to configure a STUN
7064
server that will allow it to discover its public IP address.
7165
In Elixir WebRTC this will be:
7266

@@ -86,6 +80,6 @@ And as a TURN server, you can always use our [Rel](https://github.com/elixir-web
8680

8781
If your application is deployed behind a very restrictive NAT, which should be very rare (e.g. a symmetric NAT),
8882
you will need to configure a TURN server.
89-
In most cases, TURN servers are needed on the client side as you don't have any control
83+
In most cases, TURN servers are needed on the client side as you don't have any control
9084
over a network your clients connect from.
9185
For testing and experimental purposes, you can use our publicly available TURN called [Rel](https://github.com/elixir-webrtc/rel)!

guides/deploying/fly.md

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Deploying on Fly.io
2+
3+
Elixir WebRTC-based apps can be easily deployed on [Fly.io](https://fly.io)!
4+
5+
There are just two things you need to do:
6+
7+
* configure a STUN server both on the client and server side
8+
* use a custom Fly.io IP filter on the server side
9+
10+
In theory, configuring a STUN server just on one side should be enough but we recommend doing it on both sides.
11+
12+
In JavaScript code:
13+
14+
```js
15+
pc = new RTCPeerConnection({
16+
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
17+
});
18+
```
19+
20+
In Elixir code:
21+
22+
```elixir
23+
ip_filter = Application.get_env(:your_app, :ice_ip_filter)
24+
25+
{:ok, pc} =
26+
PeerConnection.start_link(
27+
ice_ip_filter: ip_filter,
28+
ice_servers: [%{urls: "stun:stun.l.google.com:19302"}]
29+
)
30+
```
31+
32+
In `runtime.exs`:
33+
34+
```elixir
35+
if System.get_env("FLY_IO") do
36+
config :your_app, ice_ip_filter: &ExWebRTC.ICE.FlyIpFilter.ip_filter/1
37+
end
38+
```
39+
40+
In fly.toml:
41+
42+
```toml
43+
[env]
44+
# add one additional env
45+
FLY_IO = 'true'
46+
```
47+
48+
That's it!
49+
No special UDP port exports or dedicated IP address are needed.
50+
Just run `fly launch` and enjoy your deployment :)

lib/ex_webrtc/ice/fly_ip_filter.ex

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
defmodule ExWebRTC.ICE.FlyIpFilter do
2+
@moduledoc """
3+
ICE IP filter for Fly.io deployments.
4+
5+
This module defines a single function, which filters IP addresses,
6+
which ICE Agent will use as its host candidates.
7+
It accepts only the IPv4 address that `fly-global-services` resolves to.
8+
"""
9+
10+
@spec ip_filter(:inet.ip_address()) :: boolean()
11+
def ip_filter(ip_address) do
12+
case :inet.gethostbyname(~c"fly-global-services") do
13+
# Assume that fly-global-services has to resolve
14+
# to a single ipv4 address.
15+
# In other case, don't even try to connect.
16+
{:ok, {:hostent, _, _, :inet, 4, [addr]}} ->
17+
addr == ip_address
18+
19+
_ ->
20+
false
21+
end
22+
end
23+
end

lib/ex_webrtc/peer_connection/configuration.ex

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ defmodule ExWebRTC.PeerConnection.Configuration do
55

66
require Logger
77

8+
alias ExICE.ICEAgent
89
alias ExWebRTC.{RTPCodecParameters, SDPUtils}
910
alias ExSDP.Attribute.{Extmap, FMTP, RTCPFeedback}
1011

@@ -148,7 +149,7 @@ defmodule ExWebRTC.PeerConnection.Configuration do
148149
controlling_process: Process.dest(),
149150
ice_servers: [ice_server()],
150151
ice_transport_policy: :relay | :all,
151-
ice_ip_filter: (:inet.ip_address() -> boolean()),
152+
ice_ip_filter: ICEAgent.ip_filter(),
152153
ice_port_range: Enumerable.t(non_neg_integer()),
153154
audio_codecs: [RTPCodecParameters.t()],
154155
video_codecs: [RTPCodecParameters.t()],
@@ -162,7 +163,7 @@ defmodule ExWebRTC.PeerConnection.Configuration do
162163
controlling_process: Process.dest(),
163164
ice_servers: [ice_server()],
164165
ice_transport_policy: :relay | :all,
165-
ice_ip_filter: (:inet.ip_address() -> boolean()),
166+
ice_ip_filter: (:inet.ip_address() -> boolean()) | nil,
166167
ice_port_range: Enumerable.t(non_neg_integer()),
167168
audio_codecs: [RTPCodecParameters.t()],
168169
video_codecs: [RTPCodecParameters.t()],
@@ -239,7 +240,6 @@ defmodule ExWebRTC.PeerConnection.Configuration do
239240
feedbacks = Keyword.get(options, :rtcp_feedbacks, @default_rtcp_feedbacks)
240241

241242
options
242-
|> Keyword.put_new(:ice_ip_filter, fn _ -> true end)
243243
|> Keyword.put(:audio_extensions, Enum.map(audio_extensions, fn {_, ext} -> ext end))
244244
|> Keyword.put(:video_extensions, Enum.map(video_extensions, fn {_, ext} -> ext end))
245245
|> then(&struct(__MODULE__, &1))

mix.exs

+7-4
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ defmodule ExWebRTC.MixProject do
5757
defp deps do
5858
[
5959
{:ex_sdp, "~> 1.0"},
60-
{:ex_ice, "~> 0.8.0"},
60+
{:ex_ice, github: "elixir-webrtc/ex_ice"},
6161
{:ex_dtls, "~> 0.16.0"},
6262
{:ex_libsrtp, "~> 0.7.1"},
6363
{:ex_rtp, "~> 0.4.0"},
@@ -80,25 +80,28 @@ defmodule ExWebRTC.MixProject do
8080
"simulcast",
8181
"modifying",
8282
"mastering_transceivers",
83-
"deploying",
8483
"debugging"
8584
]
8685

86+
deploying_guides = ["bare", "fly"]
87+
8788
[
8889
main: "readme",
8990
logo: "logo.svg",
9091
extras:
9192
["README.md"] ++
9293
Enum.map(intro_guides, &"guides/introduction/#{&1}.md") ++
93-
Enum.map(advanced_guides, &"guides/advanced/#{&1}.md"),
94+
Enum.map(advanced_guides, &"guides/advanced/#{&1}.md") ++
95+
Enum.map(deploying_guides, &"guides/deploying/#{&1}.md"),
9496
assets: "guides/assets",
9597
source_ref: "v#{@version}",
9698
formatters: ["html"],
9799
before_closing_body_tag: &before_closing_body_tag/1,
98100
nest_modules_by_prefix: [ExWebRTC],
99101
groups_for_extras: [
100102
Introduction: Path.wildcard("guides/introduction/*.md"),
101-
Advanced: Path.wildcard("guides/advanced/*.md")
103+
Advanced: Path.wildcard("guides/advanced/*.md"),
104+
Deploying: Path.wildcard("guides/deploying/*.md")
102105
],
103106
groups_for_modules: [
104107
MEDIA: ~r"ExWebRTC\.Media\..*",

mix.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
1313
"ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"},
1414
"ex_dtls": {:hex, :ex_dtls, "0.16.0", "3ae38025ccc77f6db573e2e391602fa9bbc02253c137d8d2d59469a66cbe806b", [:mix], [{:bundlex, "~> 1.5.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2a4e30d74c6ddf95cc5b796423293c06a0da295454c3823819808ff031b4b361"},
15-
"ex_ice": {:hex, :ex_ice, "0.8.1", "4d5c911766ce92e13323b632a55d9ab821092f13fc2ebf236dc233c8c1f9a64c", [:mix], [{:elixir_uuid, "~> 1.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:ex_stun, "~> 0.2.0", [hex: :ex_stun, repo: "hexpm", optional: false]}, {:ex_turn, "~> 0.1.0", [hex: :ex_turn, repo: "hexpm", optional: false]}], "hexpm", "8f10134e2eb7e6aebbf8fba0d5fcec56d8f8db3e94c3dde045feb463979c2dda"},
15+
"ex_ice": {:git, "https://github.com/elixir-webrtc/ex_ice.git", "4c5190687e11b28a8209f2c930895647d0b2a64c", []},
1616
"ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"},
1717
"ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"},
1818
"ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"},

0 commit comments

Comments
 (0)