For a high-level overview of our Commands workflows and their use cases, be sure to have a look at our Services Overview in the Getting Started section of our docs.
Using CTO.ai Services lets you deploy long-running applications directly to our platform. Acting as a zero-configuration deployment target for your Commands or Pipelines, Services are deployed as lightweight containers (with ephemeral, non-persistent storage) for serving an application or API to the public internet over HTTP—at a unique subdomain URL created just for your service.
We currently offer Workflow SDKs with support for Python, Node, Go, and Bash runtimes. Our SDKs provide a straightforward way to send Lifecycle Event data to your CTO.ai Dashboard, interact with other workflows, and integrate with external systems.
Structure of Services Workflows
The CTO.ai ops CLI provides a subcommand to create scaffolding files for your workflow: ops init. You can use the -k flag to specify the kind as a service workflow, as well as pass an optional argument to give the workflow a name (example-service, in this case):
ops init -k service example-service
After you respond to the CLI’s interactive prompting, it will generate template code for your new Services workflow. There are three main components within the newly-created directory defining your workflow:
ops.yml
Dockerfile (and a corresponding .dockerignore file)
Your custom code
For a deeper explanation of the ops init subcommand and the scaffolding generated by our CLI, our Workflows Overview document has you covered.
Important Usage Considerations
If you plan to deploy your containerized application as a CTO.ai Service, there are a few things to keep in mind about the environment in which your application will run.
Storage and Databases
Containers deployed as CTO.ai Services do not have persistent storage. Any data written to the container’s writeable filesystem will be lost when the container is stopped. If your application requires persistent storage or databases, we recommend you configure your application to point to external storage or database systems.
⚠️
Storage is Not Persistent
Containers deployed as Services on the CTO.ai platform do not have persistent storage.
Any data written to the container’s writeable filesystem will be lost when the container is stopped.
To give your application access to your database provider, you can specify credentials for your database as Configs or Secrets in your ops.yml file, for example:
We recommend passing your database URL to your Service workflow as a Config value and your database credentials as Secrets values, to help keep them private.
Subdomains
By default, deployed Services will be assigned a unique subdomain based on the UUID of the deployment. The application deployed by the Service will be accessible at that unique URL, via the port specified in the ops.yml configuration file for the service. However, it is also possible to configure a custom domain for your Service.
Services Workflow Reference
To help you better understand how Services workflows are structured, we have included an explanation of the scaffolding template generated for each of our SDK languages below. Let’s have a look at what is generated when we create a new Services workflow with our CLI.
Create a Services Workflow (ops init)
The CTO.ai ops CLI provides a subcommand to create scaffolding files for your workflow: ops init. You can use the -k flag to specify the kind as a service workflow, as well as pass an optional argument to give the workflow a name (example-service, in this case):
ops init -k service example-service
After you respond to the CLI’s interactive prompting, it will generate template code for your new Services workflow.
Workflow Scaffolding Code
This section explains the scaffolding code that’s generated when you choose Bash as the language for your new workflow.
ops.yml and Custom Code
The values submitted in response to the interactive prompts are reflected in a newly-generated ops.yml file.
The value of the run key in a workflow definition (e.g. bash /ops/main.sh in the example below) should be the shell command that the final container image needs to run to execute your custom workflow code that runs your service. We provide a basic example workflow as part of the scaffolding template generated by our CLI (a script called main.sh for our Bash template, also included here):
version: "1"services:
- name: example-service:0.1.0description: Base Bash Service scaffolding for reference.run: bash /ops/main.shport: ["8080:8080"]
domain: ""
#!/bin/bashecho "starting server now"(while true; do echo -ne 'HTTP/1.1 200 OK\r\n\r\nOK\r\n'|./nc -l -p 8080;done)
Dockerfile and Dependencies
This is accompanied by a base Dockerfile that defines the execution environment of our workflow, and the dependencies to be installed in the final container image (via the Debian package manager or another method):
This section explains the scaffolding code that’s generated when you choose Python as the language for your new workflow.
ops.yml and Custom Code
The values submitted in response to the interactive prompts are reflected in a newly-generated ops.yml file.
The value of the run key in a workflow definition (e.g. python3 /ops/main.py in the example below) should be the shell command that the final container image needs to run to execute your custom workflow code that runs your service. We provide a basic example workflow as part of the scaffolding template generated by our CLI (a script called main.py for our Python template, also included here):
version: "1"services:
- name: example-service:0.1.0description: Base Python Service scaffolding for reference.run: python3 /ops/main.pyport: ["8080:8080"]
domain: ""
This is accompanied by a base Dockerfile that defines the execution environment of our workflow, and the dependencies to be included in the final container image (specified as a requirements.txt for our Python template):
This section explains the scaffolding code that’s generated when you choose JavaScript as the language for your new workflow.
ops.yml and Custom Code
The values submitted in response to the interactive prompts are reflected in a newly-generated ops.yml file.
The value of the run key in a workflow definition (e.g. node /ops/index.js in the example below) should be the shell command that the final container image needs to run to execute your custom workflow code to run your service. We provide a basic example workflow as part of the scaffolding template generated by our CLI (a script called index.js for our JavaScript template, also included here):
version: "1"services:
- name: example-service:0.1.0description: Base JavaScript Service scaffolding for reference.run: node /ops/index.jsport: ["8080:8080"]
domain: ""
This is accompanied by a base Dockerfile that defines the execution environment of our workflow, and the dependencies to be included in the final container image (specified as a package.json for our JavaScript template):
{
"name": "example-service",
"version": "1.0.0",
"description": "Base JavaScript Service scaffolding for reference.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1" },
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@cto.ai/sdk": "^2.4.0" }
}
node_modules
This section explains the scaffolding code that’s generated when you choose Go as the language for your new workflow.
ops.yml and Custom Code
The values submitted in response to the interactive prompts are reflected in a newly-generated ops.yml file.
The value of the run key in a workflow definition (e.g. /ops/main in the example below) should be the shell command that the final container image needs to run to execute your custom workflow code to run your service. We provide a basic example workflow as part of the scaffolding template generated by our CLI (a code file called main.go for our Go template, also included here):
version: "1"services:
- name: example-service:0.1.0description: Base Go Service scaffolding for reference.port: ["8080:8080"]
domain: ""run: /ops/main
This is accompanied by a base Dockerfile that defines the execution environment of our workflow, and the dependencies to be included in the final container image (specified as a go.mod for our Go template):
############################# Build container############################FROM golang:1.14.0-buster AS depWORKDIR /opsADD go.mod go.sum ./RUN go get ./...ADD . .RUN go build -ldflags="-s -w" -o main && strip -s main && chmod 777 main############################# Final container############################FROM registry.cto.ai/official_images/base:2-buster-slimWORKDIR /opsENV DEBIAN_FRONTEND=noninteractive
RUN apt update && apt install -y ca-certificates && rm -rf /var/lib/apt/listsCOPY --chown=ops:9999 --from=dep /ops/main .############################################################################### As a security best practice the container will always run as non-root user.##############################################################################USER ops
module github.com/cto-ai/sharedgo
go 1.14
require github.com/cto-ai/sdk-go/v2 v2.2.3