Deploy your API

    It's finally time to deploy our service to a real-life server.

    Emily supplies easy-to-use commands for deploying and re-deploying your services to both standard servers with SSH access, as well as Kubernetes clusters.

    For this tutorial, we'll use a standard Ubuntu server to which we have SSH access.

    Adding a server

    As long as the host server is running Ubuntu >= 18.04, Emily is host-agnostic. As such, any server you have SSH access to is a valid deployment target.

    The only requirements are:

    1. You know the IP or domain name to your server
    2. You have the SSH key to your server
    3. You know the username associated with the SSH key

    If you're a student, you can get credits to a large number of cloud providers through the GitHub Student Developer Pack.

    Emily needs to know about your server. To add it, simply run emily servers and choose + Add server.

    $ emily servers
    ? Emily: Add new server to emily, select a server to configure, or remove existing(Use arrow keys, confirm with ENTER)> + Add server
    - Remove server
    · + Add server
    Emily: What is the server's IP or Domain name?:
    · demo.ambolt.io
    Emily: What username should be used on the server?:
    · root
    Emily: Path to the SSH key to the server?:
    · ~/.ssh/id_rsa
    Emily: What do your want Emily to name the server?:
    · My Demo Server

    Specifying a deployment

    We very briefly mentioned deployments as a core concept in Exploring the project.

    Recall that a deployment (specified under ./deployments in your project files) specifies:

    1. What configuration to deploy
    2. Under which environment
    3. To which target

    We already have a dev configuration and dev environment, as Emily creates these by default.

    Adding an environment

    Let's create a new environment to deploy our service under called prod:

    $ emily environment
    --project my-project

    ? Emily: Please select an action:(Use arrow keys, confirm with ENTER)> + Add a new environment
    * Update an existing environment
    - Remove an existing environment
    · + Add a new environment
    Emily: Please enter the environment name:
    · prod
    Emily: Please enter a port:
    · 4242
    ? Emily: Use a GPU in this environment?(y/N) · NoEmily: Please enter the number of workers:
    · 2
    Emily: Environment "prod" added to project my-project (66AWNR)
    Created:

    - <...>/my-project/environments/prod/.emily.env

    Adding a configuration

    Let's also create a new configuration in case we need server-specific configurations:

    $ emily configuration
    --project my-project

    ? Emily: Please choose an action:(Use arrow keys, confirm with ENTER)> + Add a configuration
    - Remove a configuration
    · + Add a configuration
    ? Emily: Please select a configuration:(Use arrow keys, confirm with ENTER)
    dev
    > + Add new configuration · + Add new configuration
    Emily: Please enter the configuration name:
    · prod
    ? Emily: Please choose a configuration type(Use arrow keys, confirm with ENTER)> Docker Compose configuration
    Kubernetes configuration
    · Docker Compose configuration
    ? Emily: Please choose a Docker Compose configuration:(Use arrow keys, confirm with ENTER)> Base
    HTTPS reverse proxy (Nginx)
    External network
    · Base
    Emily: Compose configuration "prod" added to project my-project (66AWNR)
    Created:

    - <...>/my-project/configurations/prod/docker-compose.emily.yml

    Adding a deployment

    Now, let's define a new deployment using our newly created environment and configuration, targeting the server we added in the prior step:

    $ emily deployment
    --project my-project

    ? Emily: Please select an action:(Use arrow keys, confirm with ENTER)> Add a new deployment specification · Add a new deployment specification
    Emily: Please enter a deployment name:
    · my-server-deployment
    ? Emily: Please select a configuration:(Use arrow keys, confirm with ENTER)
    dev
    > prod
    + Add a new configuration
    · prod
    ? Emily: Please select an environment:(Use arrow keys, confirm with ENTER)
    dev
    > prod
    + Add a new environment
    · prod
    ? Emily: Please select the type of deployment to create a specification for:(Use arrow keys, confirm with ENTER)
    Local - deploy on your local system
    > Server - deploy to a server with SSH access
    Kubernetes - deploy to a Kubernetes cluster
    · Server - deploy to a server with SSH access
    ? Emily: Select or create a server:(Use arrow keys, confirm with ENTER)
    + Add server
    > Name: My Demo Server
    IP/Domain: demo.ambolt.io
    Status: Online
    · Name: My Demo Server
    IP/Domain: demo.ambolt.io
    Status: Online

    Emily: Server deployment "my-server-deployment" added to project my-project (66AWNR)
    Created:

    - <...>/my-project/deployments/my-server-deployment/deploy.sh
    - <...>/my-project/deployments/my-server-deployment/deployment.json
    - <...>/my-project/deployments/my-server-deployment/post_deployment_hook.sh

    Deploying your service

    Now that we have specified a deployment targeting our server, there's nothing left to do expect execute the deployment. To do this, run the emily deploy command:

    $ emily deploy
    --project my-project

    ? Emily: Please select a deployment specification:(Use arrow keys, confirm with ENTER)
    local
    > my-server-deployment
    + Add new deployment specification
    · my-server-deployment
    ? Emily: Use the specified deployment and configuration?(Use arrow keys, confirm with ENTER)> Current
    - Environment: prod
    - Configuration: prod

    + Choose different environment/configuration

    · Current
    - Environment: prod
    - Configuration: prod


    Emily: Deploying my-project (66AWNR) using:

    Deployment: my-server-deployment (server)
    Environment: prod
    Configuration: prod

    Checking internet connection...

    Verifying the deployment

    Once the Emily command finishes, your service should be successfully deployed.

    You can verify your deployment by visiting your API at http://:4242/.

    If you cannot connect to your API just yet, don't worry! There might be a couple of easy-to-fix issues getting in the way.

    Ensuring open ports

    You might not be able to connect to your API because the specified port 4242 is not open on the server. If you're using a server from an open cloud provider, e.g. DigitalOcean, take the following steps to allow port 4242 traffic through the firewall:

    1. SSH into your server
    2. Run ufw allow 22 to allow OpenSSH connections
    3. Run ufw allow 4242 to allow connections on port 4242
    4. Run ufw enable

    If you're using a more managed cloud provider, e.g. Azure or AWS, you may need to allow traffic by adding the necessary network security rules in the Azure or AWS control dashboards.

    Ensuring IP is whitelisted

    If you're using a managed cloud provider like Azure or AWS, connections to your server might be limited to an IP whitelist that your current IP is not on. Consult your provider's documentation on allowing traffic.

    Testing your API

    Let's try and get a stock market prediction out of our API. Execute the following request:

    curl -X POST 'http://<your-ip-or-domain>:4242/predict' \ --header 'Content-Type: application/json' \ --data-raw '{ "ticker": "AAPL", "open": 19.1, "high": 25.312, "low": 18.2 }'

    If you receive a reponse similar to the following, your API is officially live:

    { "close": 0.7231, "confidence": 0.01323 }

    Congratulations!

    Your API should now be live and accessible by the rest of your infrastructure.

    While this is fine for testing, this isn't quite production-ready since all communication to your API remains unencrypted and, therefore, unsafe. Let's fix that in the next section: Securing your API with HTTPS and Nginx.