In this tutorial you will learn how to interactively use the SpecElektra
specification
language and kdb
to write a configuration specification for dockerd.
- Already know how to write a specification
- how to create and mount a specification using
kdb
- how to add keys with different types, defaults, enums, array specifications, wildcard specifications and examples to your specification and how to validate them
- use
kdb
to create and mount a specification for dockerd - define defaults, array / wildcard specifications, examples and checks for keys in the validation
- use the specification as a starting point for customizing the configuration of installed applications
In this tutorial we will introduce a possible specification for dockerd.
Using kdb
we will configure the specification.
NOTE: As the specification for dockerd is quite big we will only present a sample for the above mentioned metakeys and link to a full specification dockerd-full-spec.
Before we start just an overview of the structure:
- Specification file location:
/docker/daemon.json
- Parent specification key:
spec:/sw/dockerd/dockerd/#0/current
Elektra
supports multiple types which leads to a more flexible specification.
See type plugin for information about all the types that are supported.
We will be mounting an existing example dockerd-spec.
First you need to mount a specification file, in this case dockerd.ini
to the spec:/
namespace.
You can define the path inside the spec:/
namespace as /sw/docker/dockerd/#0/current
, refer to
the documentation to find out more about constructing the name.
sudo kdb mount "$PWD/examples/spec/dockerd.ini" spec:/sw/docker/dockerd/#0/current ni
# RET: 0
NOTE: If you encounter any error saying that you have already mounted some specification with the same name you can run
sudo kdb umount spec:/sw/docker/dockerd/#0/current
and rerun the above command.
Note: ni is the format which is used for the specification in the file. You can also choose to use json, then you need use yajl instead of ni.
Next you can define, that this specification uses a specific mountpoint for a concrete application configuration.
So you can say the concrete configuration should be written to dockerd.ini
.
kdb meta-set spec:/sw/dockerd/dockerd/#0/current mountpoint /docker/daemon.json
# RET: 0
Your dockerd.ini
file should now contain the mountpoint
metakey:
NOTE: Excerpt of
cat $(kdb file spec:/sw/docker/dockerd/#0/current)
.
# ;Ni1
# ; Generated by the ni plugin using Elektra (see libelektra.org).
# =
# []
# meta:/mountpoint = /dockerd/daemon.json
Next we will define that our configuration should be written json
.
We can do this by running:
kdb meta-set spec:/sw/dockerd/dockerd/#0/current infos/plugin "yajl"
# RET: 0
sudo kdb spec-mount "/sw/docker/dockerd/#0/current" ni
# RET: 0
This specification mount makes sure that the paths where the concrete configuration should be (daemon.json
)
are ready to fulfill our specification (dockerd.ini
).
Be aware that different files get mounted for different namespaces.
You've a specification file (dockerd.ini
) for the spec
-namespace and three files (daemon.json
) on different locations
for the dir
- user
- and system
-namespaces.
You can see the files by providing the namespace as prefix to the kdb file
command (each shows a different path):
kdb file system:/sw/docker/dockerd/#0/current
# /dockerd/daemon.json
kdb file user:/sw/docker/dockerd/#0/current
# STDOUT-REGEX: /dockerd/daemon.json
kdb file dir:/sw/docker/dockerd/#0/current
# STDOUT-REGEX: /dockerd/daemon.json
NOTE: The $PWD should equal the PWD where you run
sudo kdb mount "$PWD/examples/spec/dockerd.ini" spec:/sw/docker/dockerd/#0/current ni
.
Note: The files only exist, when configuration values are stored there, i.e. they are created on the first
kdb set
and removed with the lastkdb rm
.
For more information about namespaces in Elektra please see here, a tutorial about the topic is available here.
NOTE: All output we display for
cat $(kdb file spec:/sw/docker/dockerd/#0/current)
is an excerpt of the whole file output.
In this example for dockerd
we will be using 3 types of specifications:
- Simple specification (type and description)
- Enum specifications (for keys were only a set of possible options can be used)
- Array and wildcard specifications (for keys where a list of possible options can be used)
As the dockerd
specification is big we will just present one of each of the above mentioned specification types.
NOTE: In Elektra we use
/
instead of-
to seperate key names. This results in a hierarchical structure of key names. This commands will automatically store the specification in thedockerd-daemon.ni
specification file.
[data/root]
meta:/type = string
meta:/description = Root directory of persistent Docker state
meta:/default = /var/lib/docker
In order to get the above specification we will need the following commands:
kdb meta-set spec:/sw/docker/dockerd/#0/current/data/root type "string"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/data/root description "Root directory of persistent Docker state"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/data/root default "/var/lib/docker"
# RET: 0
In case no data/root
key gets configured the value /var/lib/docker
is used.
Let us verify that the metakeys have been set correctly:
NOTE: Excerpt of
cat $(kdb file spec:/sw/docker/dockerd/#0/current)
.
cat $(kdb file spec:/sw/docker/dockerd/#0/current)
# [data/root]
# meta:/type = string
# meta:/description = Root directory of persistent Docker state
# meta:/default = /var/lib/docker
[default/cgroupns/mode]
meta:/description = Default mode for containers cgroup namespace
meta:/default = private
meta:/check/enum = #1
meta:/check/enum/#0 = host
meta:/check/enum/#1 = private
In order to get the above specification we will need the following commands:
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/cgroupns/mode description "Default mode for containers cgroup namespace"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/cgroupns/mode default "private"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/cgroupns/mode check/enum "#1"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/cgroupns/mode check/enum/#0 "host"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/cgroupns/mode check/enum/#1 "private"
# RET: 0
With this configuration we have managed to allow two possible values for default/cgroupns/mode
.
The values are host
and private
.
The default value if we do not set any configuration is private
.
Let us verify that the metakeys have been set correctly:
NOTE: Excerpt of
cat $(kdb file spec:/sw/docker/dockerd/#0/current)
.
cat $(kdb file spec:/sw/docker/dockerd/#0/current)
# [default/cgroupns/mode]
# meta:/check/enum/#0 = host
# meta:/check/enum/#1 = private
# meta:/description = Default mode for containers cgroup namespace
# meta:/default = private
# meta:/check/enum = #1
# [data/root]
# meta:/type = string
# meta:/description = Root directory of persistent Docker state
# meta:/default = /var/lib/docker
[default/ulimits/_]
meta:/type = long
meta:/description = Default ulimits for containers
meta:/example = 64000
For this specification we want to allow an arbitrary number of default ulimits
.
The name of the ulimits
does not matter but all should have the same metakeys.
In order to get the above specification we will need following commands:
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/ulimits/_ type "long"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/ulimits/_ description "Default ulimits for containers"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/ulimits/_ example "64000"
# RET: 0
The above specification will allow us to create any name below the default/ulimits
key.
The value needs to be a string.
The sample value is 64000
.
Let us verify that the metakeys have been set correctly:
NOTE: Excerpt of
cat $(kdb file spec:/sw/docker/dockerd/#0/current)
.
cat $(kdb file spec:/sw/docker/dockerd/#0/current)
# [default/ulimits/_]
# meta:/type = long
# meta:/example = 64000
# meta:/description = Default ulimits for containers
# [default/cgroupns/mode]
# meta:/check/enum/#0 = host
# meta:/check/enum/#1 = private
# meta:/description = Default mode for containers cgroup namespace
# meta:/default = private
# meta:/check/enum = #1
# [data/root]
# meta:/type = string
# meta:/description = Root directory of persistent Docker state
# meta:/default = /var/lib/docker
[default/address/pools]
meta:/array/min = 0
meta:/description = Default address pools for node specific local networks (list)
[default/address/pools/#/base]
meta:/type = string
meta:/description = Ip address (ipv4) + subnet
meta:/example = 172.30.0.0/16
[default/address/pools/#/size]
meta:/type = short
meta:/description = Number of ip addresses in this pool with base
meta:/example = 24
The specification above shows the use of an array specification with the #
character.
We define the array to have a minimum value of 0
and arbitrary max length.
We use type
, description
and example
as metakeys on the keys beneath each array element.
This configuration above assures that we can configure pools
with base
and size
.
It prevents a configuration like:
default/address/pools/#0/size = "test"
It will fail as default/address/pools/#/size
is required to be of type short
when set.
In order to get the above specification we will need following commands:
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/address/pools array/min "0"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/address/pools description "Default address pools for node specific local networks (list)"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/address/pools/#/base type "string"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/address/pools/#/base description "Ip address (ipv4) + subnet"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/address/pools/#/base example "172.30.0.0/16"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/address/pools/#/size type "short"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/address/pools/#/size description "Number of ip addresses in this pool with base"
# RET: 0
kdb meta-set spec:/sw/docker/dockerd/#0/current/default/address/pools/#/size example "24"
# RET: 0
The above specification defines that we can create array elements and each can have base
or size
.
Your specification should be complete now! After adding all the keys that are necessary for our application, you can verify that all specification keys are contained by running:
cat $(kdb file spec:/sw/docker/dockerd/#0/current)
NOTE: We want display the output because it is too long to display (~ 400-500 lines).
The above tutorial has given a good overview of how to write a specification.
You might want to add a full example specification for dockerd
using kdb import
.
To do so, follow the next steps.
To make sure we don't run into errors we will clean up everything we have done by now.
sudo kdb rm -r spec:/sw/docker/dockerd/#0/current
sudo kdb umount spec:/sw/docker/dockerd/#0/current
rm -rf $PWD/dockerd
(make sure that you are in the same PWD as when you run thesudo kdb mount
)
Now we are going to add an example of dockerd-full-spec.
Make sure you are in the root of the cloned libelektra
repository:
sudo kdb mount "$PWD/dockerd/dockerd-daemon.ni" spec:/sw/docker/dockerd/#0/current ni
kdb meta-set spec:/sw/dockerd/dockerd/#0/current mountpoint /dockerd/daemon.ni
kdb meta-set spec:/sw/dockerd/dockerd/#0/current infos/plugin "yajl"
sudo kdb spec-mount "/sw/docker/dockerd/#0/current"
sudo kdb import spec:/sw/docker/dockerd/#0/current ni < ./examples/spec/dockerd.ini
To verify that everything was created successfully, run:
cat $(kdb file spec:/sw/docker/dockerd/#0/current)
NOTE: We want display the output because it is too long to display (~ 400-500 lines).
The full specification can be viewed at dockerd-full-spec.