table(table table-bordered table-condensed).
|_. Key|_. Type|_. Description|_. Notes|
|partitions|array of strings|The names of one or more compute partitions that may run this container. If not provided, the system will choose where to run the container.|Optional.|
-|preemptible|boolean|If true, the dispatcher will ask for a preemptible cloud node instance (eg: AWS Spot Instance) to run this container.|Optional. Default is false.|
+|preemptible|boolean|If true, the dispatcher should use a preemptible cloud node instance (eg: AWS Spot Instance) to run this container. Whether a preemptible instance is actually used "depends on cluster configuration.":{{site.baseurl}}/admin/spot-instances.html|Optional. Default is false.|
|max_run_time|integer|Maximum running time (in seconds) that this container will be allowed to run before being cancelled.|Optional. Default is 0 (no limit).|
h2. Configuration
-Add entries to @InstanceTypes@ that have @Preemptible: true@. Typically you want to add both preemptible and non-preemptible entries for each cloud provider VM type. The @Price@ for preemptible instances is the maximum bid price, the actual price paid is dynamic and will likely be lower. For example:
+First, configure some @InstanceTypes@ that have @Preemptible: true@. For a preemptible instance, @Price@ determines the maximum bid price; the actual price paid is dynamic and will likely be lower.
+
+Typically you want to add both preemptible and non-preemptible entries for each cloud provider VM type. To do this automatically, use @PreemptiblePriceFactor@ to enable a preemptible version of each listed type, using the given factor to set the maximum bid price relative to the non-preemptible price. Alternatively, you can configure preemptible instance types explicitly. For example, the following two configurations are equivalent:
<pre>
Clusters:
ClusterID:
+ Containers:
+ PreemptiblePriceFactor: 0.8
InstanceTypes:
m4.large:
- Preemptible: false
ProviderType: m4.large
VCPUs: 2
RAM: 8GiB
AddedScratch: 32GB
Price: 0.1
- m4.large.spot:
- Preemptible: true
+</pre>
+
+<pre>
+Clusters:
+ ClusterID:
+ InstanceTypes:
+ m4.large:
ProviderType: m4.large
VCPUs: 2
RAM: 8GiB
AddedScratch: 32GB
Price: 0.1
+ m4.large.preemptible:
+ Preemptible: true
+ ProviderType: m4.large
+ VCPUs: 2
+ RAM: 8GiB
+ AddedScratch: 32GB
+ Price: 0.08
</pre>
Next, you can choose to enable automatic use of preemptible instances:
<pre>
+Clusters:
+ ClusterID:
Containers:
AlwaysUsePreemptibleInstances: true
</pre>
#
# This flag is ignored if no preemptible instance types are
# configured, and has no effect on top-level containers.
- AlwaysUsePreemptibleInstances: true
+ AlwaysUsePreemptibleInstances: false
+
+ # Automatically add a preemptible variant for every
+ # non-preemptible entry in InstanceTypes below. The maximum bid
+ # price for the preemptible variant will be the non-preemptible
+ # price multiplied by PreemptiblePriceFactor. If 0, preemptible
+ # variants are not added automatically.
+ #
+ # A price factor of 1.0 is a reasonable starting point.
+ PreemptiblePriceFactor: 0
# PEM encoded SSH key (RSA, DSA, or ECDSA) used by the
# cloud dispatcher for executing containers on worker VMs.
"Containers.MaxDispatchAttempts": false,
"Containers.MaxRetryAttempts": true,
"Containers.MinRetryPeriod": true,
+ "Containers.PreemptiblePriceFactor": false,
"Containers.ReserveExtraRAM": true,
"Containers.RuntimeEngine": true,
"Containers.ShellAccess": true,
}
}
+ // Preprocess/automate some configs
+ for id, cc := range cfg.Clusters {
+ ldr.autofillPreemptible("Clusters."+id, &cc)
+
+ if strings.Count(cc.Users.AnonymousUserToken, "/") == 3 {
+ // V2 token, strip it to just a secret
+ tmp := strings.Split(cc.Users.AnonymousUserToken, "/")
+ cc.Users.AnonymousUserToken = tmp[2]
+ }
+
+ cfg.Clusters[id] = cc
+ }
+
// Check for known mistakes
for id, cc := range cfg.Clusters {
for remote := range cc.RemoteClusters {
return nil, err
}
}
- if strings.Count(cc.Users.AnonymousUserToken, "/") == 3 {
- // V2 token, strip it to just a secret
- tmp := strings.Split(cc.Users.AnonymousUserToken, "/")
- cc.Users.AnonymousUserToken = tmp[2]
- }
}
return &cfg, nil
}
}
}
}
+
+func (ldr *Loader) autofillPreemptible(label string, cc *arvados.Cluster) {
+ if factor := cc.Containers.PreemptiblePriceFactor; factor > 0 {
+ for name, it := range cc.InstanceTypes {
+ if !it.Preemptible {
+ it.Preemptible = true
+ it.Price = it.Price * factor
+ it.Name = name + ".preemptible"
+ if it2, exists := cc.InstanceTypes[it.Name]; exists && it2 != it {
+ ldr.Logger.Warnf("%s.InstanceTypes[%s]: already exists, so not automatically adding a preemptible variant of %s", label, it.Name, name)
+ continue
+ }
+ cc.InstanceTypes[it.Name] = it
+ }
+ }
+ }
+
+}
func (s *LoadSuite) TestNoWarningsForDumpedConfig(c *check.C) {
var logbuf bytes.Buffer
- logger := logrus.New()
- logger.Out = &logbuf
cfg, err := testLoader(c, `
Clusters:
zzzzz:
_, err = ldr.Load()
c.Assert(err, check.ErrorMatches, `there is no default storage class.*`)
}
+
+func (s *LoadSuite) TestPreemptiblePriceFactor(c *check.C) {
+ yaml := `
+Clusters:
+ z1111:
+ InstanceTypes:
+ Type1:
+ RAM: 12345M
+ VCPUs: 8
+ Price: 1.23
+ z2222:
+ Containers:
+ PreemptiblePriceFactor: 0.5
+ InstanceTypes:
+ Type1:
+ RAM: 12345M
+ VCPUs: 8
+ Price: 1.23
+ z3333:
+ Containers:
+ PreemptiblePriceFactor: 0.5
+ InstanceTypes:
+ Type1:
+ RAM: 12345M
+ VCPUs: 8
+ Price: 1.23
+ Type1.preemptible: # higher price than the auto-added variant would use -- should generate warning
+ ProviderType: Type1
+ RAM: 12345M
+ VCPUs: 8
+ Price: 1.23
+ Preemptible: true
+ Type2:
+ RAM: 23456M
+ VCPUs: 16
+ Price: 2.46
+ Type2.preemptible: # identical to the auto-added variant -- so no warning
+ ProviderType: Type2
+ RAM: 23456M
+ VCPUs: 16
+ Price: 1.23
+ Preemptible: true
+`
+ var logbuf bytes.Buffer
+ cfg, err := testLoader(c, yaml, &logbuf).Load()
+ c.Assert(err, check.IsNil)
+ cc, err := cfg.GetCluster("z1111")
+ c.Assert(err, check.IsNil)
+ c.Check(cc.InstanceTypes["Type1"].Price, check.Equals, 1.23)
+ c.Check(cc.InstanceTypes, check.HasLen, 1)
+
+ cc, err = cfg.GetCluster("z2222")
+ c.Assert(err, check.IsNil)
+ c.Check(cc.InstanceTypes["Type1"].Preemptible, check.Equals, false)
+ c.Check(cc.InstanceTypes["Type1"].Price, check.Equals, 1.23)
+ c.Check(cc.InstanceTypes["Type1.preemptible"].Preemptible, check.Equals, true)
+ c.Check(cc.InstanceTypes["Type1.preemptible"].Price, check.Equals, 1.23/2)
+ c.Check(cc.InstanceTypes["Type1.preemptible"].ProviderType, check.Equals, "Type1")
+ c.Check(cc.InstanceTypes, check.HasLen, 2)
+
+ cc, err = cfg.GetCluster("z3333")
+ c.Assert(err, check.IsNil)
+ // Don't overwrite the explicitly configured preemptible variant
+ c.Check(cc.InstanceTypes["Type1.preemptible"].Price, check.Equals, 1.23)
+ c.Check(cc.InstanceTypes, check.HasLen, 4)
+ c.Check(logbuf.String(), check.Matches, `(?ms).*Clusters\.z3333\.InstanceTypes\[Type1\.preemptible\]: already exists, so not automatically adding a preemptible variant of Type1.*`)
+ c.Check(logbuf.String(), check.Not(check.Matches), `(?ms).*Type2\.preemptible.*`)
+ c.Check(logbuf.String(), check.Not(check.Matches), `(?ms).*(z1111|z2222)[^\n]*InstanceTypes.*`)
+}
cd /usr/src/arvados/sdk/cwl/tests
exec ./arvados-tests.sh $@
elif [[ "$suite" = "conformance-v1.2" ]] ; then
- exec cwltest --tool arvados-cwl-runner --test conformance_tests.yaml -N307 -- \$EXTRA
+ exec cwltest --tool arvados-cwl-runner --test conformance_tests.yaml -N307 $@ -- \$EXTRA
else
exec ./run_test.sh RUNNER=arvados-cwl-runner EXTRA="\$EXTRA" $@
fi
StaleLockTimeout Duration
SupportedDockerImageFormats StringSet
AlwaysUsePreemptibleInstances bool
+ PreemptiblePriceFactor float64
RuntimeEngine string
LocalKeepBlobBuffersPerVCPU int
LocalKeepLogsToContainerLog string
end
end
- test "Having preemptible_instances=true create a committed child container request and verify the scheduling parameter of its container" do
+ test "AlwaysUsePreemptibleInstances makes child containers preemptible" do
+ Rails.configuration.Containers.AlwaysUsePreemptibleInstances = true
common_attrs = {cwd: "test",
priority: 1,
command: ["echo", "hello"],
--- /dev/null
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+{%- set curr_tpldir = tpldir %}
+{%- set tpldir = 'arvados' %}
+{%- from "arvados/map.jinja" import arvados with context %}
+{%- set tpldir = curr_tpldir %}
+
+extra_shell_sudo_passwordless_sudo_pkg_installed:
+ pkg.installed:
+ - name: sudo
+
+extra_shell_sudo_passwordless_config_file_managed:
+ file.managed:
+ - name: /etc/sudoers.d/arvados_passwordless
+ - makedirs: true
+ - user: root
+ - group: root
+ - mode: '0440'
+ - replace: false
+ - contents: |
+ # This file managed by Salt, do not edit by hand!!
+ # Allow members of group sudo to execute any command without password
+ %sudo ALL=(ALL:ALL) NOPASSWD:ALL
+ - require:
+ - pkg: extra_shell_sudo_passwordless_sudo_pkg_installed
--- /dev/null
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+{%- set curr_tpldir = tpldir %}
+{%- set tpldir = 'arvados' %}
+{%- from "arvados/map.jinja" import arvados with context %}
+{%- set tpldir = curr_tpldir %}
+
+extra_shell_sudo_passwordless_sudo_pkg_installed:
+ pkg.installed:
+ - name: sudo
+
+extra_shell_sudo_passwordless_config_file_managed:
+ file.managed:
+ - name: /etc/sudoers.d/arvados_passwordless
+ - makedirs: true
+ - user: root
+ - group: root
+ - mode: '0440'
+ - replace: false
+ - contents: |
+ # This file managed by Salt, do not edit by hand!!
+ # Allow members of group sudo to execute any command without password
+ %sudo ALL=(ALL:ALL) NOPASSWD:ALL
+ - require:
+ - pkg: extra_shell_sudo_passwordless_sudo_pkg_installed
# Same when using self-signed certificates.
SKIP_SNAKE_OIL="dont_add_snakeoil_certs"
fi
- for f in $(ls "${F_DIR}"/extra/extra/*.sls | grep -v ${SKIP_SNAKE_OIL}); do
+ for f in $(ls "${F_DIR}"/extra/extra/*.sls | egrep -v "${SKIP_SNAKE_OIL}|shell_sudo_passwordless"); do
echo " - extra.$(basename ${f} | sed 's/.sls$//g')" >> ${S_DIR}/top.sls
done
# Use byo or self-signed certificates
grep -q "custom_certs" ${S_DIR}/top.sls || echo " - extra.custom_certs" >> ${S_DIR}/top.sls
fi
+ echo " - extra.shell_sudo_passwordless" >> ${S_DIR}/top.sls
echo " - postgres" >> ${S_DIR}/top.sls
echo " - docker.software" >> ${S_DIR}/top.sls
echo " - arvados" >> ${S_DIR}/top.sls
;;
"shell")
# States
+ echo " - extra.shell_sudo_passwordless" >> ${S_DIR}/top.sls
grep -q "docker" ${S_DIR}/top.sls || echo " - docker.software" >> ${S_DIR}/top.sls
grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
# Pillars