Jobs
Jobs are the primary configuration that users interact with when using Nomad. A job is a declarative specification of tasks that Nomad should run. Jobs have a globally unique name, one or many task groups, which are themselves collections of one or many tasks.
The format of the jobs is documented in the job specification. They can either be specified in HashiCorp Configuration Language or JSON, however we recommend only using JSON when the configuration is generated by a machine.
Running a Job
Before starting, let’s run nomad in development mode so we don’t have to setup a cluster for this example. For such purpose you can run the command below:
nomad agent -dev &> nomad.log &
All logs will be forwarded to nomad.log
in case you want to check them.
Once nomad is running, we will use the init
command which
generates a skeleton job file:
nomad init
You can view the contents of this file by running cat example.nomad
. In this
example job file, we have declared a single task ‘redis’ which is using
the Docker driver to run the task. The primary way you interact with Nomad
is with the run
command. The run
command takes
a job file and registers it with Nomad. This is used both to register new
jobs and to update existing jobs.
We can register our example job now:
nomad run example.nomad
Anytime a job is updated, Nomad creates an evaluation to determine what actions need to take place. In this case, because this is a new job, Nomad has determined that an allocation should be created and has scheduled it on our local agent.
To inspect the status of our job we use the status
command:
nomad status example
ALLOC_ID=$(nomad status example | tail -n 1 | cut -d ' ' -f1)
Here we can see that the result of our evaluation was the creation of an allocation that is now running on the local node.
An allocation represents an instance of Task Group placed on a node. To inspect
an allocation we use the alloc-status
command:
nomad alloc-status $ALLOC_ID
We can see that Nomad reports the state of the allocation as well as its
current resource usage. By supplying the -stats
flag, more detailed resource
usage statistics will be reported.
To see the logs of a task, we can use the logs command:
nomad logs $ALLOC_ID redis
Modifying a Job
The definition of a job is not static, and is meant to be updated over time. You may update a job to change the docker container, to update the application version, or to change the count of a task group to scale with load.
For now, edit the example.nomad
file to update the count and set it to 3:
# The "count" parameter specifies the number of the task groups that should
# be running under this group. This value must be non-negative and defaults
# to 1.
count = 3
sed -i 's/count = 1/count = 3/' example.nomad
Once you have finished modifying the job specification, use the plan
command to invoke a dry-run of the scheduler to see
what would happen if you ran the updated job:
nomad plan example.nomad
We can see that the scheduler detected the change in count and informs us that
it will cause 2 new instances to be created. The in-place update that will
occur is to push the update job specification to the existing allocation and
will not cause any service interruption. We can then run the job with the
run command the plan
emitted.
By running with the -check-index
flag, Nomad checks that the job has not
been modified since the plan was run. This is useful if multiple people are
interacting with the job at the same time to ensure the job hasn’t changed
before you apply your modifications.
nomad run -check-index 6 example.nomad
Because we set the count of the task group to three, Nomad created two additional allocations to get to the desired state. It is idempotent to run the same job specification again and no new allocations will be created.
Now, let’s try to do an application update. In this case, we will simply change
the version of redis we want to run. Edit the example.nomad
file and change
the Docker image from “redis:3.2” to “redis:4.0”:
# Configure Docker driver with the image
config {
image = "redis:4.0"
}
sed -i 's/redis:3.2/redis:4.0/' example.nomad
We can run plan
again to see what will happen if we submit this change:
nomad plan example.nomad
The plan output shows us that one allocation will be updated and that the other
two will be ignored. This is due to the max_parallel
setting in the update
stanza, which is set to 1 to instruct Nomad to perform only a single change at
a time.
Once ready, use run
to push the updated specification:
nomad run example.nomad
After running, the rolling upgrade can be followed by running nomad status
and
watching the deployed count.
We can see that Nomad handled the update in three phases, only updating a single
allocation in each phase and waiting for it to be healthy for min_healthy_time
of 10 seconds before moving on to the next. The update strategy can be
configured, but rolling updates makes it easy to upgrade an application at large
scale.
Stopping a Job
So far we’ve created, run and modified a job. The final step in a job lifecycle
is stopping the job. This is done with the stop
command:
nomad stop example
When we stop a job, it creates an evaluation which is used to stop all
the existing allocations. If we now query the job status, we can see it is
now marked as dead (stopped)
, indicating that the job has been stopped and
Nomad is no longer running it:
nomad status example
If we wanted to start the job again, we could simply run
it again.
Next Steps
Users of Nomad primarily interact with jobs, and we’ve now seen how to create and scale our job, perform an application update, and do a job tear down. Next we will add another Nomad client to create our first cluster