Requests and Deploys

Singularity uses the concepts of a Request and Deploy to keep track of changes to a particular task or set of related tasks.

A Request can be thought of as the high level information for a single project or deployable item. For example, the name and owner's email for a single web service, or the name and cron schedule for a single scheduled job.

A Deploy can then be thought of as the specific configuration or version of the running code for that deployable item.

To illustrate the differences between and usages of these two concepts, we will walk through an example using Singularity to run a single web service.

The TestService Example

Creating a Request

You have a new web service called TestService that you want to run via Singularity. The first thing you need to do is create a Request for TestService. To create this request, you would POST json over http to the Singularity API (/api/requests) or create a request via the new request page in the Singularity UI. Example json:

{
    "id": "TestService",
    "requestType": "SERVICE",
    "owners": ["me@test.com"],
    "instances": 1,
}

This Request now holds high level information about your web service. Singularity knows it is a long running SERVICE named TestService and that you want to run 1 instance of this service.

Creating a Deploy

Now you want TestService to actually run. To do this, you need to create a Deploy for the TestService Request. This deploy will let Singularity know all of the information necessary to actually build and launch a task. This information includes things like the command to run, environment variables to set, the location of any artifacts to download, or the resources that should be allocated to a task. You would create this deploy by POSTing json to the Singularity API's deploy endpoint (/api/deploys), or creating a new deploy in the Singularity UI. Example json:

{
  "deploy": {
    "requestId":"TestService",
    "id":"5",
    "resources": {
        "cpus":1,
        "memoryMb":128,
        "numPorts":2
    }, 
    "command":"java -Ddw.server.applicationConnectors[0].port=$PORT0 -Ddw.server.adminConnectors[0].port=$PORT1 -jar singularitytest-1.0-SNAPSHOT.jar server example.yml",
    "uris": [
        "https://github.com/HubSpot/singularity-test-service/releases/download/1.0/singularitytest-1.0-SNAPSHOT.jar",
        "https://github.com/HubSpot/singularity-test-service/releases/download/1.0/example.yml"
    ],
    "healthcheck": {
       "uri": "/"
     }
  }
}

Posting this to Singularity creates a PendingDeploy. Singularity will then try to build and launch tasks using the information provided in the Deploy json (i.e. a task with those artifacts that needs 1 cpu, 128MB of memory, etc and is run with that command). Singularity will also know to only build and launch 1 of these tasks based on the number of instances you set in the Request earlier. Once that task is launched and Singularity determines it is healthy, the Deploy succeeds and the Deploy json you provided is now the ActiveDeploy.

A New Deploy

Let's say some changes were made to the TestService code and you want to run the new version in Singularity, maybe with a bit more memory as well. You would create a new Deploy json with information about the new code to run, and updated memory value and POST that to the Singularity API (/api/deploys), or create a new deploy in the Singularity UI.

Singularity sees a new Deploy has been started and makes that the PendingDeploy for the TestService Request. Singularity will then try to build and launch 1 new task with the configuration specified in the new PendingDeploy (note that the task from the previous Deploy is still running and it's settings are unchanged). Once the task is launched and determined to be healthy, that new Deploy now becomes the ActiveDeploy and the task from the old deploy is shut down.

Updating the Request

Now, for example, you notice TestService is getting more traffic than expected and you want to run 3 instances instead of just 1. You can PUT a new number of instances over http to the Singularity API (/api/requests/request/{requestId}/scale), or click Scale on the Singularity UI request page and enter a new number of instances to update our number of instances. Example json:

{
    "id": "TestService",
    "requestType": "SERVICE",
    "owners": ["me@test.com"],
    "instances": 3,
}

Singularity sees this update to the request and will try to build and launch 2 additional tasks so that there are 3 total instances running. The configuration/resources/command/etc for these new tasks will be determined from the current ActiveDeploy. In other words, there is no need to re-deploy TestService to scale the number of instances up or down. This information is saved separate from the more detailed information in the deploy. If you were to create another new deploy after scaling, Singularity would then build and launch 3 new tasks for that new deploy.