Deploying A Rust Server to Google App Engine with Travis CI & Docker

Credit: https://rocket.rs/

I woke up this morning with one goal: to convert my website (morgangallant.com) into a Rust web server. I have been a long time fan of Google App Engine (GAE), and have used it in several personal projects, therefore it seemed logical to use it to host my website too.

Little did I know that deploying Rust code to Google App Engine would take almost an entire day of psychological pain to successfully accomplish.

My overall goal was to setup Travis CI with my public Github repository, to build, test and then deploy my rust application to GAE. I used multiple tutorials throughout the day, yet none fully encapsulated the entire process of deploying the code. That being said, some parts of those tutorials were particularly useful, so credit will be provided as necessary.

Things you will need for this tutorial (go and set them up):


Rocket Server Setup

To perform my initial environment setup, I created a simple “Hello World” web application in Rust using the Rocket framework. This tutorial assumes you have some experience programming in Rust, and using the cargo tool.

I first created a new project named mg_website using cargo, then proceeded to make the following modifications to the project:

Cargo.toml

[package]
name = "mg_website"
version = "0.1.0"
authors = [Redacted]
[dependencies]
rocket = "*"
rocket_codegen = "*"

src/main.rs

#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
#[get("/")]
fn index() -> &'static str {
"Hello, world! This is a test. If you see this message, it worked."
}
fn main() {
rocket::ignite().mount("/", routes![index]).launch();
}

I also added a Rocket.toml file in the root of the project, containing the following base options (some fields have been redacted):

[development]
address = "localhost"
port = 8000
limits = { forms = 32768 }
[production]
address = "0.0.0.0"
port = 8080
limits = { forms = 32768 }

It is extremely important that your production port for Rocket is set to 8080, or else Google App Engine will throw 503 errors. I learned this the hard way.

Next, I created a new Github repository called mg_website and added it as a remote to my project.

git init
git remote add origin <GITHUB-REPO>
git pull origin master --allow-unrelated-histories
git add -A
git commit -m "Initial Commit"
git push origin master

To verify everything is working, do cargo run and ensure that Rocket is running on localhost:8000.


Docker

Due to the lack of native Rust support from GAE, a docker image must be used to package the project. Add a Dockerfile to your project, and add the following code:

FROM centos:7
MAINTAINER Denis Kolodin <deniskolodin@gmail.com>
EXPOSE 8080
ENV SOURCES=/sources
RUN yum update -y
RUN yum install -y file gcc openssl-devel
RUN curl -sSf https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly --disable-sudo
RUN mkdir -p $SOURCES
ADD ./ $SOURCES
WORKDIR $SOURCES
RUN cargo build --release
CMD ROCKET_ENV=production ./target/release/mg_website

Credit: https://github.com/DenisKolodin

I altered Denis’ dockerfile slightly, adding the channel=nightly command to the Rust installation, as well as specified production mode when running the server.

By using this code, Docker will automatically install Rust, build the project and run the rust server in production mode. This is what allows GAE to actually run the Rust code. Thanks Denis.


Google App Engine & Travis

My goal is to allow Travis to automatically deploy my app to App Engine once a build is successful. To do this, I had to authenticate travis with Google Cloud, while maintaining my personal security (I used a public Github Repo).

First, go to the Google Cloud Console > APIs & Services > Google App Engine Admin API. You will need to enable this API to allow Travis to push new builds onto App Engine.

Next, go to Credentials > Add Credential > Service Account Key, and download a .json file. I renamed it secure.json for the purposes of this tutorial. This is a very sensitive file, so never in any circumstances publicise it’s contents. We will use Travis CI to encrypt this file.

In your project directory, create a new .travis.yml file. Then, execute the following command to encrypt the secure json token and add it to your travis.yml file:

travis encrypt-file /path/to/secure/json --add

This will add secure.json.enc to your project and update your .travis.yml with the instructions to unencrypt it. Inside your travis file, it will have something along the lines of -out ~/\secure.json -d. Just change this to -out ~/secure.json -d.

From here, we can populate the rest of our Travis configuration file with the following:

language: rust
rust:
- nightly
before_install:
- [REDACTED]
deploy:
provider: gae
keyfile: ~/secure.json
project: <APP-ENGINE-PROJECT-NAME>
skip_cleanup: true

The redacted part just contains the encryption part, auto generated by travis.

Finally, we can create our simple app.yaml file for Google App Engine containing the following information:

runtime: custom
env: flex

The custom runtime option makes GAE automatically look for a Dockerfile when building, which is what we want.


Your project should now look like this:

.
├── Cargo.toml
├── Dockerfile
├── Rocket.toml
├── app.yaml
├── secure.json.enc
└── src
└── main.rs

The last step will be to enable TravisCI on your git repository, and push your repository to Github.

Enabling Travis CI for Git Repository
git add -A
git commit -m "Initialized CI"
git push origin master

If everything worked, you should now begin seeing Travis building & deploying your application whenever files are committed. Note that these builds can take up to 20 minutes due to the large compilation times of Rust dependencies.


Conclusion

In this tutorial, I have hopefully provided a guide that all users can understand and use, to allow easier compilation and deployment of web servers written in Rust. I spent the better part of six hours setting this up myself, due to a long string of errors that needed fixing. I hope that this tutorial can help someone else out, to avoid the pain I suffered.

If you have any questions or concerns, or if something didn’t work as expected, tweet me at gallantmrgn and I will be sure to help you out.

As for web servers in Rust, I do not fully understand their potential, and I am excited to begin experimenting with mine. Good luck!