Runtime-Bridge
Runtime-Bridge manages the allocations of containers used to execute codes remotely from web IDE. It launches an AWS Fargate container for each user on request, and returns its public IP address and port number through which the user can access the container.
This server also provides reverse proxy using NGINX to allow the users to access the container using HTTPs connection.
What it does
/api/containers/launch
Identify the user with JWT in the request header. If a container is already allocated, return its information. Otherwise, launch new AWS Fargate container by calling AWS SDK for ECS and wait until it has an ENI(Elastic Network Interface) to get public IP address.
After public IP is attached to it, runtime-bridge sends ping to the runtime-agent inside of the container periodically until it responses. When runtime-bridge receives it, it sends another POST request to initialize the agent server. Lastly, it stores initialized data (e.g. username and password used for SSH) into the database, and responds to the requesting user with IP address and port number of the container.
/api/containers/info
Use an API key in the request header that is unique to all containers to retrieve whose container sends the request. If the container is in an available status, return its information for runtime-agent to connect SSH locally.
/api/runtimes/available
Return available languages that runtime-containers have prepared for.
Reverse proxy for the container
location ~ /([0-9\.]+)/(\d+)/(.*/?) {
...
set $target http://$1:$2/$3$is_args$args;
proxy_pass $target;
...
}
In nginx.conf
, there is a reverse proxy configuration in order to allow users to connect to their containers using HTTPs.
Unfortunately, this reverse proxy rule works for any public IP address and port number regardless of whether they are of a container or not.
Note
It would be more appropriate and more secure to launch a container in a private subnet. If implemented like this, it may eliminate the waiting time spent to get the public IP at launch.
Because there is NGINX server providing reverse proxy to the containers, it is also possible to forward users' requests to the containers that are in the private subnet. In this case, you can rewrite the target URL ($target
in nginx.conf
) to forward requests only to the subnet area, which is more strict and less vulnerable way.
Requirements
Development
- Configure AWS credentials. Check out here to see how to do this.
- Copy and modify dotenv file
$ cp .env.example .env
- Start server
$ gin --port 8080 run main.go --all
Deployment
- Build app
$ GOOS=linux GOARCH=amd64 go build -o app .
You may need to change GOARCH
to suit your server.
- Copy created executable to a server. For instance, you can use
scp
.
$ scp -i <key_path> ./app <username>@<ip_addr>:/path/to/app
- Run executable in the server:
$ PORT=8080 GIN_MODE=release /path/to/app
- Make sure you have configured NGINX at
/etc/nginx/...
configuration files may exist in several places. See here for more details.
API document
API documents are generated by gin-swagger. To update the documents, run $ swag init
.
DB Migration
For database migration, I used golang-migrate. It would be helpful to Check CLI document too.
-
Install golang-migrate CLI as a root
$ curl -L https://packagecloud.io/golang-migrate/migrate/gpgkey | apt-key add -
$ echo "deb https://packagecloud.io/golang-migrate/migrate/ubuntu/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/migrate.list
$ apt-get update
$ apt-get install -y migrate
-
When you want to modify DB schema, create migration scripts
$ migrate create -ext sql -dir db/migrations -seq <title>
-
Write DDL at the created up/down.sql files
-
Run a migration
# This example is only applicable for Mysql and MariaDB.
$ migrate -verbose -database "mysql://<user>:<url_encoded_password>@tcp(<host>:<port>>)/<db_name>" -path db/migrations up # or down
Database urls must be encoded string. See more details from here. Also, there are database-specific formats for each engine.