Elixir on Google Cloud Platform and App Engine
cloud.google.com
cloud.google.com
* Elixir clients for Google APIs: https://github.com/GoogleCloudPlatform/elixir-google-api
* Elixir runtime for App Engine: https://github.com/GoogleCloudPlatform/elixir-runtime
* Elixir examples: https://github.com/GoogleCloudPlatform/elixir-samples
On a related note, there goes my weekend.
App Engine is a platform-as-a-service (PaaS) which generally means a lot of things are done for you. You give App Engine the code, and App Engine builds it, deploys it, scales it, monitors it, applies OS patches, and generally provides a bunch of related services for you.
Compute Engine is more infrastructure-as-a-service (IaaS) which means the service gives you VMs, and you handle installation, build, deployment, and setting up load balancing, scaling, monitoring, etc. the way you want.
There are also hybrid products that fall somewhere in the middle. Kubernetes Engine is an example. Indeed, you can locate most hosting platforms somewhere on this spectrum.
Note also that there are trade-offs specific to Elixir as well. PaaSes generally have to be opinionated about application architecture. Most PaaSes assume a stateless web application well suited for container-based deployment, which runs somewhat counter to Erlang's stateful services and hot code swapping. If you're writing a typical web app (say, using Phoenix) then a PaaS like App Engine will often work very well. But if you are doing something different and managing persistent nodes and processes, you may need to control deployment yourself, which makes a lower level service like Compute Engine more useful.
Outside of the main repository, or on google open source projects, or etc, people can generally do what they want, they just don't get the benefits of the above if they aren't using a language we already support very well.
Supported or used or being experimented with or what?
Also, why use App Engine over Compute Engine for Elixir? I understand "if you want more control" but can you elaborate about what kind of "more control" someone would want that App Engine doesn't offer?
Disclaimer: I work for Google Cloud
This would help handle an important use case for me: ability to keep thousands of (client) websocket connections alive across deployments, to avoid missing a data update.
Thanks!
https://github.com/wende/elchemy
It allows you to write elm-like code that transpiles into easy to read and idiomatic (for the most part) elixir code.
I love the philosophy and robustness of the BEAM VM, but I can't go back to the stone ages of a language with a poor (or non-existent) static type system.
What you probably want is a strong type system. Functional programming languages (I'm thinking Julia as an archetype of strongly typed systems) especially are really good at dispatching over types, and can make robust optimizing inferences using a well-designed type system.
BEAM languages are strongly typed, but there are far fewer types (and no true custom types) like julia. However, gating based on functional guards is simply amazing and more than makes up for a limited type system in terms of code clarity. As for compile-time vs runtime error trapping - part of the point of BEAM languages is "who cares?" An error doesn't bring down your VM. It gets logged, you can see it, and apply a hot patch and fix it if you need to.
It won't bring down your VM, but it will impact any end-users of your service. Not all errors can be solved by restarting a process or retrying a request (but I recognize the argument that you should aim to catch those types of errors in tests because they are part of the core business logic).
Also just wanted to note that hot-patching has serious costs. Code upgrades/downgrades can be difficult to write and the upgrades themselves also need testing (and this often doesn't get the attention it needs).
There are no silver bullets, basically.
Expressive, algebraic types allow me to tell my compiler, IDE, and code heirs what I'm trying to do so they know just by reading my code if I failed. That's indispensible.
When you connect to a cluster to send to something on another node, it’s no different than making an API call to a service that you don’t control. The compiler can’t make any guarantees there without contracts in hand, which can change at a whim on a deploy or when new nodes are added.
For message passing across a cluster, setting up Dialyzer is probably the most realistic approach. Function calls on the BEAM are more like request routing I think.
Node -> Module -> Function -> Arity -> Pattern
As these things change, the contracts have to be exchanged with every member of the cluster which would not only add significant communication overhead, it would also bypass those compile time guarantees.
That's before even considering things like hot deploys where the code is updated live, while running, mid-execution.
The contract enforcement becomes akin to the goals of WSDL, which added a ton of overhead with minimal benefit aside from making consuming APIs easier for statically typed languages because of generators.
Is the overhead worth the difference from...
Client (this message will fail on this server) -> don't send
to...
Client -> (this message failed)
Especially when talking about a cluster which you own...that benefit is questionable at best.
It doesn't cover every case, but it covers enough of them to provide a very watchful eye without getting in your way.
https://speakerdeck.com/jvoegele/elixirconf-2016-dialyzer-op...
A static verification framework for message passing in Go using behavioural types, Lange et al., ICSE 18
http://blog.acolyer.org/2018/01/25/a-static-verification-fra...
:)
I recall someone on here (HN) saying that many of Elixir's strengths are weakened significantly because K8 does similar things (auto distribution, hot reloading, etc.)
Any truth to this? Also, can you distribute nodes on VMs and deploy them to Google Cloud?
---
As a side note, is there a front-end equivalent to Elixir? Elm is functional but is non intuitive. Elixirscript is a thing but seems a bit too early.
Elixir is exceptional at stateful apps. And it is true, many of those things are now handled through K8S. However, K8S doesn't handle everything and requires a lot more ceremony and infrastructure than what you can get within the Elixir/Erlang ecosystem.
I think there might be a way to work with Elixir + K8S in a way specific to the domain you are working with. Not everyone needs to be able to hot-reload code without dropping live connections, but if if you do, and you want to deploy on K8S, then you can try writing a K8S operator that encapsulates that idea. However: https://gravitational.com/blog/running-postgresql-on-kuberne... (which is more about how every distributed system requires deep domain knowledge, not just specifically about PostgreSQL)
I have not deployed Elixir on GCP. I have deployed other things though, and I can tell you the private networking is much better set up than AWS. As far as distributing nodes, you can do that with any of the cloud providers.
Purescript?
But there's still a lot from Kubernetes that Elixir can take advantage of. For example - service discovery, autoscaling, clustering, secret management - all made easier with a K8s deployment and Elixir.
I wrote a few blog posts about it a while ago if you're interested: https://medium.com/polyscribe/a-complete-guide-to-deploying-...
This is similar to an app server in Ruby. A single worker might die, but it's still being managed by a central coordinator. If the coordinator dies, everything is down and the app restarts
BTW, it's very hard to bring BEAM down. Most exceptions will bring kill the faulty process, but leave the supervisor tree untouched. Only "unmanaged" code (for example, calling a C library using a NIF) might crash the whole VM.
Then again, no technology is infallible.
> (a process within the VM can die and K8s will have no idea)
I suppose that I see nothing wrong with this and would say that it's not weird or unusual to be the case. They're separate things, although they both achieve visually similar goals (keep things running as much as possible).
My point is less about K8s vs Elixir but rather a statement that this seems acceptable and shouldn't be a reason to rule out something like running Elixir on K8s.
Sounds like heart: http://erlang.org/doc/man/heart.html
Not completely true. Try to allocate more memory than available and then see what happens.
Also, isn't ENOMEM a standard error that you can handle using the standard error handling techniques?
BEAM VM comes up and tries to connect to the database. The database is temporarily unreachable from that machine, but only the Ecto process dies and BEAM VM doesn't die. Since the last process (BEAM) in the Dockerfile is running, Kubernetes thinks it's healthy and will try sending traffic to it.
Compare this to something like node, where the node process will die if it can't connect to the database, and Kubernetes won't try to direct traffic to it and will restart it for you. You can do this with Elixir supervisors as well - that's why I said Kubernetes replicates some of Elixir's functionality.
It's definitely possible to work around all this which is why we ended up using Kubernetes for our Elixir deployment (and I'd recommend it). It's just these little things that make it clear that Kubernetes was designed with something like node in mind and not BEAM.
[1] https://kubernetes.io/docs/tasks/configure-pod-container/con...
Once Kubernetes sends an exit signal to the VM, the VM will start the shutdown of its processes, and everything should terminate gracefully.
It is OK that Kubernetes doesn't know about Elixir processes terminating, because it is not its job. It is the job of the Elixir software to express its start-up and shutdown guarantees. If you don't want the VM to start when you don't have a database connection, then you should start a process right after your connection pool starts and before your endpoint runs that attempts to issue a query and act accordingly.
The biggest thing that this does is give you first class access to the ecosystem APIs and with native setups for AppEngine (Heroku-ish), Compute Engine (do it yourself on a VM) and a provider in Gigalixir which runs on GCP that gives a Heroku-like experience but is tailored specifically to everything Elixir needs (hot loading, clustering out of the box, etc).
Between those two things, GCP is pretty strong.
I was reading up on Gigalixir thanks to this thread, is it basically a more "batteries included" version of AppEngine specifically targetted for Elixir?
It’s just nice to see a hosting option that tailors itself to the more unique needs of the BEAM when the rest of the industry is focusing on stateless containers.
"We are on GCP now and have no plans to migrate off of it, or go 'multi-cloud'"
[1]: https://www.reddit.com/r/discordapp/comments/7mv84u/does_dis...
(Disclosure: I work on Google Cloud)
The ability to connect between nodes, avoid mandatory restart, avoid connection limits and keep websocket connections alive easily gives compute engine a significant advantage over platforms like Heroku.
As far as I understand Heroku lets you connect between nodes (Dynos within a private space with dns discovery [0]) and also supports websockets [1]... for the mandatory restart, wouldn't you want to design your process as stateless anyway?
[0] https://devcenter.heroku.com/articles/dyno-dns-service-disco... [1] https://devcenter.heroku.com/articles/websockets
Sorry if I'm missing something obvious!
Maybe you can convince the powers that be to do something about these. :)
With most things you’d want stateless. Erlang/Elixir is designed with 3 built in databases, the ability to hold states on isolated processes and per process supervision and recovery. It’s a different animal than most.
It is wonderful that Heroku now offers exec, which at least allows using observer and other remote debugging tools. The tooling keeps getting better—but there is no denying that it was designed for Ruby deployment, and working the BEAM has significant production differences.
That's exactly it. The value proposition for Erlang & Elixir is essentially that they provide great abstractions for managing state and they make great back-end languages for creating stateful applications.
But hey, they have Elixir support !?!?
It's a pretty small group right now. I'm hoping that a new venue and push will help bring in more people.
Libcluster looks good too. Everything bitwalker touches seems to be solid AF.
I spent a lot of time last week trying to get elixir setup on gcloud, but variables were the one thing I could never figure out and I eventually gave up.
Is there some magical way to get variables and secrets in gcloud without having to commit them to a repo?
Compute Engine is just raw IaaS, like EC2. You "store secrets" on GCE by writing them to a volume and attaching it across your cluster, or putting them in a common database. Or just pushing data onto your instances after they're up using Fabric or something. You know, just like if they were real computers.
App Engine is made to be a complete and self-contained PaaS (much moreso than anything like Heroku), to the point that it doesn't really use external credentials for anything. For example, you can access BigQuery tables from App Engine without supplying any "BigQuery credentials." Oddly enough, if you do need to connect to third-party services (or, say, Google Cloud SQL) from App Engine, they recommend that you store the credentials to do so in... a BigQuery table. GAE is proprietary and weird. It's basically its own "abstract machine."
Kubernetes Engine, in comparison, is exactly for the use-case of "deploying workloads" of regular potentially-stateful, potentially-long-lived POSIX apps that need things like DB connection strings passed to them. If you're a modern backend developer who burns Docker images like people 10 years ago burned CDs, Kubernetes is the service you should be using on any compute cloud you interact with. Everything else is an impedance mismatch.