Configuration¶
Skyward does not read a configuration file. The control plane target is resolved for each client or CLI call.
Control plane resolution¶
The resolution order is:
- an explicit
urlor--url; SKYWARD_URL;- the daemon at
http://127.0.0.1:17590.
Compute starts a daemon at that address when none answers, printing no server is running, starting it now. The daemon detaches, so it outlives the process that started it and goes on reconciling the machines it bought. Stop it with sky server stop. The CLI never starts one: it reports that nothing answers.
A daemon that runs a different version of Skyward than the client is warned about and used: the same routes may carry other wire types, and a type the two disagree on fails where it is read. sky.Options(strict_version=True) refuses it instead, before a machine is bought; stop it and let the client start one, or point the client at a daemon on its version.
Passing database= to Compute runs the control plane inside the current process over that file instead of reaching a daemon. sky server start --database gives a daemon its own. Either way the default is ~/.skyward/skyward.sqlite, and a database is ignored when a URL is given.
import skyward as sky
# The daemon at 127.0.0.1:17590, started here if nobody has.
with sky.Compute(provider=sky.Container()) as compute:
result = train(data) >> compute
# The control plane in this process, over a database of its own.
with sky.Compute(provider=sky.Container(), database="/tmp/experiment.sqlite") as compute:
result = train(data) >> compute
# Remote daemon.
with sky.Compute(provider=sky.AWS(), url="http://127.0.0.1:17590") as compute:
result = train(data) >> compute
The Compute client and the CLI use the same resolution rules. Inspect the resolved values with:
Provider accounts¶
Provider accounts resolve credentials in the client process. A provider descriptor contains the provider kind, its non-secret configuration, credentials, and an optional account name. When a compute starts, the descriptor registers the named account with the daemon if it does not exist. Credentials are not returned by provider read operations.
provider = sky.AWS(name="production", region="us-east-1")
with sky.Compute(provider=provider, accelerator="A100") as compute:
train(data) >> compute
Use a distinct name for multiple accounts of the same provider kind. Provider configuration belongs to the account, not to a global singleton.
Compute configuration¶
The public configuration objects are composed at the Compute boundary:
Specdescribes one provider and hardware alternative;Optionscontrols timeouts, retries, health checks, and autoscaling;Executorcontrols task execution on each node;Image,Volume, andPortdescribe the node environment and local tunnels.
See Compute and task dispatch for the full Python surface and Cloud providers for account credentials and offer caching.
skyward.Provider
¶
Bases: Struct
A provider account: which cloud, and what it takes to log in.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
The account's alias, and the identity of the provider row. Two accounts of the same kind coexist by having two names. Empty is the kind itself. |
skyward.Image
¶
Bases: Struct
The environment a node builds before it runs anything.
The base, the interpreter, the packages and where they resolve from. What the
user shipped from their own machine is not here — includes is packed into
a blob client-side and only its hash travels, because a spec is written to the
compute row and served back by the API.
base = None
class-attribute
instance-attribute
¶
python = None
class-attribute
instance-attribute
¶
pip = ()
class-attribute
instance-attribute
¶
apt = ()
class-attribute
instance-attribute
¶
pip_indexes = ()
class-attribute
instance-attribute
¶
env = field(default_factory=dict)
class-attribute
instance-attribute
¶
shell_vars = field(default_factory=dict)
class-attribute
instance-attribute
¶
includes = ()
class-attribute
instance-attribute
¶
excludes = ()
class-attribute
instance-attribute
¶
includes_sha256 = None
class-attribute
instance-attribute
¶
The user-code tarball, once the client has built it and put it in the blob
store. includes/excludes are the client's inputs; this is what the node
reads.
metrics = None
class-attribute
instance-attribute
¶
What the node measures about itself: :data:READINGS when None, exactly the list otherwise.
A :data:Reading is served by the node's own collector, a :class:MetricSpec by a
loop of its own. A name may appear once, whichever kind it is.
bootstrap_timeout = 900
class-attribute
instance-attribute
¶
skyward = 'auto'
class-attribute
instance-attribute
¶
warm = False
class-attribute
instance-attribute
¶
Whether a machine that finished bootstrapping is kept as a boot image.
Off because what it creates is never removed: an AMI holds a snapshot that bills
for its storage until it is deregistered, and nothing here deregisters it. Turning
it on is taking that on. What is created carries :meth:content_hash as a tag, on
the image and on the snapshot behind it, so it can be found again and removed.
Only providers that can snapshot a running machine honor it.
mutable = False
class-attribute
instance-attribute
¶
Whether the image of a compute that is up may be changed, best-effort.
Only :data:MUTABLE may change, and a node that is ready with another image is
sent through bootstrapping again and comes back ready. A package removed from
pip is not promised to leave the machine. A compute built without it keeps the
contract it always had: a different image is a different compute.
MUTABLE = frozenset({'pip', 'pip_indexes', 'includes', 'excludes', 'includes_sha256'})
class-attribute
¶
The fields a PATCH may change on a mutable image.
__post_init__()
¶
content_hash(source)
¶
Name the environment a bootstrapped machine ends up in.
Covers what the bootstrap installs — the base, the interpreter, the packages
and the indexes they are resolved from — together with source, which is
what stands in for a skyward version now that a node installs whatever the
daemon is running.
Left out is everything the bootstrap re-applies on every boot: the exports, the shell vars, the metric commands, and the user code, which is synced per run. Folding those in would split the images over changes that cost nothing to redo.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
:attr: |
required |
Returns:
| Type | Description |
|---|---|
str
|
Twelve hex characters — long enough to name an image, short enough to read in one. |
digest()
¶
Name exactly this image, every field included.
A node reports the digest of the image it materialized, and the reconciler compares
it with the spec's to tell when the spec asks for another one. Unlike
:meth:content_hash it is not about warm images, and it includes what the
bootstrap re-applies: the exports, the shell vars, the metrics and the includes.
Returns:
| Type | Description |
|---|---|
str
|
The sha256 of the image's deterministic JSON encoding, in full. |