website: intro in progress

This commit is contained in:
Mitchell Hashimoto 2014-07-23 14:06:47 -07:00
parent 8517d0950b
commit b8b5f15f3a
10 changed files with 335 additions and 720 deletions

View File

@ -1,125 +0,0 @@
---
layout: "intro"
page_title: "Run the Agent"
sidebar_current: "gettingstarted-agent"
---
# Run the Terraform Agent
After Terraform is installed, the agent must be run. The agent can either run
in a server or client mode. Each datacenter must have at least one server,
although 3 or 5 is recommended. A single server deployment is _**highly**_ discouraged
as data loss is inevitable in a failure scenario. [This guide](/docs/guides/bootstrapping.html)
covers bootstrapping a new datacenter. All other agents run in client mode, which
is a very lightweight process that registers services, runs health checks,
and forwards queries to servers. The agent must be run for every node that
will be part of the cluster.
## Starting the Agent
For simplicity, we'll run a single Terraform agent in server mode right now:
```
$ terraform agent -server -bootstrap -data-dir /tmp/consul
==> WARNING: Bootstrap mode enabled! Do not enable unless necessary
==> WARNING: It is highly recommended to set GOMAXPROCS higher than 1
==> Starting Terraform agent...
==> Starting Terraform agent RPC...
==> Terraform agent running!
Node name: 'Armons-MacBook-Air'
Datacenter: 'dc1'
Server: true (bootstrap: true)
Client Addr: 127.0.0.1 (HTTP: 8500, DNS: 8600, RPC: 8400)
Cluster Addr: 10.1.10.38 (LAN: 8301, WAN: 8302)
==> Log data will now stream in as it occurs:
[INFO] serf: EventMemberJoin: Armons-MacBook-Air.local 10.1.10.38
[INFO] raft: Node at 10.1.10.38:8300 [Follower] entering Follower state
[INFO] terraform: adding server for datacenter: dc1, addr: 10.1.10.38:8300
[ERR] agent: failed to sync remote state: rpc error: No cluster leader
[WARN] raft: Heartbeat timeout reached, starting election
[INFO] raft: Node at 10.1.10.38:8300 [Candidate] entering Candidate state
[INFO] raft: Election won. Tally: 1
[INFO] raft: Node at 10.1.10.38:8300 [Leader] entering Leader state
[INFO] terraform: cluster leadership acquired
[INFO] terraform: New leader elected: Armons-MacBook-Air
[INFO] terraform: member 'Armons-MacBook-Air' joined, marking health alive
```
As you can see, the Terraform agent has started and has output some log
data. From the log data, you can see that our agent is running in server mode,
and has claimed leadership of the cluster. Additionally, the local member has
been marked as a healthy member of the cluster.
<div class="alert alert-block alert-warning">
<strong>Note for OS X Users:</strong> Terraform uses your hostname as the
default node name. If your hostname contains periods, DNS queries to
that node will not work with Terraform. To avoid this, explicitly set
the name of your node with the <code>-node</code> flag.
</div>
## Cluster Members
If you run `terraform members` in another terminal, you can see the members of
the Terraform cluster. You should only see one member (yourself). We'll cover
joining clusters in the next section.
```
$ terraform members
Armons-MacBook-Air 10.1.10.38:8301 alive role=terraform,dc=dc1,vsn=1,vsn_min=1,vsn_max=1,port=8300,bootstrap=1
```
The output shows our own node, the address it is running on, its
health state, and some metadata associated with the node. Some important
metadata keys to recognize are the `role` and `dc` keys. These tell you
the service name and the datacenter that member is within. These can be
used to lookup nodes and services using the DNS interface, which is covered
shortly.
The output from the `members` command is generated based on the
[gossip protocol](/docs/internals/gossip.html) and is eventually consistent.
For a strongly consistent view of the world, use the
[HTTP API](/docs/agent/http.html), which forwards the request to the
Terraform servers:
```
$ curl localhost:8500/v1/catalog/nodes
[{"Node":"Armons-MacBook-Air","Address":"10.1.10.38"}]
```
In addition to the HTTP API, the
[DNS interface](/docs/agent/dns.html) can be used to query the node. Note
that you have to make sure to point your DNS lookups to the Terraform agent's
DNS server, which runs on port 8600 by default. The format of the DNS
entries (such as "Armons-MacBook-Air.node.terraform") will be covered later.
```
$ dig @127.0.0.1 -p 8600 Armons-MacBook-Air.node.terraform
...
;; QUESTION SECTION:
;Armons-MacBook-Air.node.terraform. IN A
;; ANSWER SECTION:
Armons-MacBook-Air.node.terraform. 0 IN A 10.1.10.38
```
## Stopping the Agent
You can use `Ctrl-C` (the interrupt signal) to gracefully halt the agent.
After interrupting the agent, you should see it leave the cluster gracefully
and shut down.
By gracefully leaving, Terraform notifies other cluster members that the
node _left_. If you had forcibly killed the agent process, other members
of the cluster would have detected that the node _failed_. When a member leaves,
its services and checks are removed from the catalog. When a member fails,
its health is simply marked as critical, but is not removed from the catalog.
Terraform will automatically try to reconnect to _failed_ nodes, which allows it
to recover from certain network conditions, while _left_ nodes are no longer contacted.
Additionally, if an agent is operating as a server, a graceful leave is important
to avoid causing a potential availability outage affecting the [consensus protocol](/docs/internals/consensus.html).
See the [guides section](/docs/guides/index.html) to safely add and remove servers.

View File

@ -0,0 +1,203 @@
---
layout: "intro"
page_title: "Build Infrastructure"
sidebar_current: "gettingstarted-build"
---
# Build Infrastructure
With Terraform installed, let's dive right into it and start creating
some infrastructure.
We'll build infrastructure on
[AWS](http://aws.amazon.com) for the getting started guide
since it is popular and generally understood, but Terraform
can [manage many providers](#),
including multiple providers in a single configuration.
Some examples of this are in the
[use cases section](/intro/use-cases.html).
If you don't have an AWS account,
[create one now](http://aws.amazon.com/free/).
For the getting started guide, we'll only be using resources
which qualify under the AWS
[free-tier](http://aws.amazon.com/free/),
meaning it will be free.
If you already have an AWS account, you may be charged some
amount of money, but it shouldn't be more than a few dollars
at most.
<div class="alert alert-block alert-warning">
<p>
<strong>Note:</strong> If you're not using an account that qualifies
under the AWS
<a href="http://aws.amazon.com/free/">free-tier</a>,
you may be charged to run these examples. The most you should
be charged should only be a few dollars, but we're not responsible
for any charges that may incur.
</p>
</div>
## Configuration
The set of files used to describe infrastructure in Terraform is simply
known as a Terraform _configuration_. We're going to write our first
configuration now to launch a single AWS EC2 instance.
The format of the configuration files is
[documented here](#).
Configuration files can
[also be JSON](#), but we recommend only using JSON when the
configuration is generated by a machine.
The entire configuration is shown below. We'll go over each part
after. Save the contents to a file named `example.tf`. Verify that
there are no other `*.tf` files in your directory, since Terraform
loads all of them.
```
provider "aws" {
access_key = "ACCESS_KEY_HERE"
secret_key = "SECRET_KEY_HERE"
region = "us-east-1"
}
resource "aws_instance" "example" {
ami = "ami-408c7f28"
instance_type = "t1.micro"
}
```
Replace the `ACCESS_KEY_HERE` and `SECRET_KEY_HERE` with your
AWS access key and secret key, available from
[this page](https://console.aws.amazon.com/iam/home?#security_credential).
We're hardcoding them for now, but will extract these into
variables later in the getting started guide.
This is a complete configuration that Terraform is ready to apply.
The general structure should be intuitive and straightforward.
The `provider` block is used to configure the named provider, in
our case "aws." A provider is responsible for creating and
managing resources. Multiple provider blocks can exist if a
Terraform configuration is comprised of multiple providers,
which is a common situation.
The `resource` block defines a resource that exists within
the infrastructure. A resource might be a physical component such
as an EC2 instance, or it can be a logical resource such as
a Heroku applcation.
The resource block has two strings before opening the block:
the resource type and the resource name. In our example, the
resource type is "aws\_instance" and the name is "example."
The prefix of the type maps to the provider. In our case
"aws\_instance" automatically tells Terraform that it is
managed by the "aws" provider.
Within the resource block itself is configuration for that
resource. This is dependent on each resource provider and
is fully documented within our
[providers reference](#). For our EC2 instance, we specify
an AMI for Ubuntu, and request a "t1.micro" instance so we
qualify under the free tier.
## Execution Plan
Next, let's see what Terraform would do if we asked it to
apply this configuration. In the same directory as the
`example.tf` file you created, run `terraform plan`. You
should see output similar to what is copied below. We've
truncated some of the output to save space.
```
$ terraform plan
...
+ aws_instance.example
ami: "" => "ami-408c7f28"
availability_zone: "" => "<computed>"
instance_type: "" => "t1.micro"
key_name: "" => "<computed>"
private_dns: "" => "<computed>"
private_ip: "" => "<computed>"
public_dns: "" => "<computed>"
public_ip: "" => "<computed>"
security_groups: "" => "<computed>"
subnet_id: "" => "<computed>"
```
`terraform plan` shows what changes Terraform will apply to
your infrastructure given the current state of your infrastructure
as well as the current contents of your configuration.
If `terraform plan` failed with an error, read the error message
and fix the error that occurred. At this stage, it is probably a
syntax error in the configuration.
The output format is similar to the diff format generated by tools
such as Git. The output has a "+" next to "aws\_instance.example",
meaning that Terraform will create this resource. Beneath that,
it shows the attributes that will be set. When the value it is
going to is `<computed>`, it means that the value won't be known
until the resource is created.
## Apply
The plan looks good, our configuration appears valid, so its time to
create real resources. Run `terraform apply` in the same directory
as your `example.tf`, and watch it go! It will take a few minutes
since Terraform waits for the EC2 instance to become available.
```
$ terraform apply
aws_instance.example: Creating...
ami: "" => "ami-408c7f28"
instance_type: "" => "t1.micro"
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
...
```
Done! You can go to the AWS console to prove to yourself that the
EC2 instance has been created.
Terraform also put some state into the `terraform.tfstate` file
by default. This state file is extremely important; it maps various
resource metadata to actual resource IDs so that Terraform knows
what it is managing. This file must be saved and distributed
to anyone who might run Terraform. We recommend simply putting it
into version control, since it generally isn't too large.
You can inspect the state using `terraform show`:
```
$ terraform show
aws_instance.example:
id = i-e60900cd
ami = ami-408c7f28
availability_zone = us-east-1c
instance_type = t1.micro
key_name =
private_dns = domU-12-31-39-12-38-AB.compute-1.internal
private_ip = 10.200.59.89
public_dns = ec2-54-81-21-192.compute-1.amazonaws.com
public_ip = 54.81.21.192
security_groups.# = 1
security_groups.0 = default
subnet_id =
```
You can see that by creating our resource, we've also gathered
a lot more metadata about it. This metadata can actually be referenced
for other resources or outputs, which will be covered later in
the getting started guide.
## Next
Congratulations! You've built your first infrastructure with Terraform.
You've seen the configuration syntax, an example of a basic execution
plan, and understand the state file.
Next, we're going to move on to changing and destroying infrastructure.

View File

@ -0,0 +1,95 @@
---
layout: "intro"
page_title: "Change Infrastructure"
sidebar_current: "gettingstarted-change"
---
# Change Infrastructure
In the previous page, you created your first infrastructure with
Terraform: a single EC2 instance. In this page, we're going to
modify that resource, and see how Terraform handles change.
Infrastructure is continuously evolving, and Terraform was built
to help manage and enact that change. As you change Terraform
configurations, Terraform builds an execution plan that only
modifies what is necessary to reach your desired state.
By using Terraform to change infrastructure, you can version
control not only your configurations but also your state so you
can see how the infrastructure evolved over time.
## Configuration
Let's modify the `ami` of our instance. Edit the "aws\_instance.web"
resource in your configuration and change it to the following:
```
resource "aws_instance" "example" {
ami = "ami-aa7ab6c2"
instance_type = "t1.micro"
}
```
We've changed the AMI from being an Ubuntu 14.04 AMI to being
an Ubuntu 12.04 AMI. Terraform configurations are meant to be
changed like this. You can also completely remove resources
and Terraform will know to destroy the old one.
## Execution Plan
Let's see what Terraform will do with the change we made.
```
$ terraform plan
...
-/+ aws_instance.example
ami: "ami-408c7f28" => "ami-aa7ab6c2" (forces new resource)
availability_zone: "us-east-1c" => "<computed>"
key_name: "" => "<computed>"
private_dns: "domU-12-31-39-12-38-AB.compute-1.internal" => "<computed>"
private_ip: "10.200.59.89" => "<computed>"
public_dns: "ec2-54-81-21-192.compute-1.amazonaws.com" => "<computed>"
public_ip: "54.81.21.192" => "<computed>"
security_groups: "" => "<computed>"
subnet_id: "" => "<computed>"
```
The prefix "-/+" means that Terraform will destroy and recreate
the resource, versus purely updating it in-place. While some attributes
can do in-place updates (which are shown with a "~" prefix), AMI
changing on EC2 instance requires a new resource. Terraform handles
these details for you, and the execution plan makes it clear what
Terraform will do.
Additionally, the plan output shows that the AMI change is what
necessitated the creation of a new resource. Using this information,
you can tweak your changes to possibly avoid destroy/create updates
if you didn't want to do them at this time.
## Apply
From the plan, we know what will happen. Let's apply and enact
the change.
```
$ terraform apply
aws_instance.example: Destroying...
aws_instance.example: Modifying...
ami: "ami-408c7f28" => "ami-aa7ab6c2"
Apply complete! Resources: 0 added, 1 changed, 1 destroyed.
...
```
As the plan predicted, Terraform started by destroying our old
instance, then creating the new one. You can use `terraform show`
again to see the new properties associated with this instance.
## Next
You've now seen how easy it is to modify infrastructure with
Terraform. Feel free to play around with this more before continuing.
In the next section we're going to destroy our infrastructure.

View File

@ -1,94 +0,0 @@
---
layout: "intro"
page_title: "Registering Health Checks"
sidebar_current: "gettingstarted-checks"
---
# Health Checks
We've now seen how simple it is to run Terraform, add nodes and services, and
query those nodes and services. In this section we will continue by adding
health checks to both nodes and services, a critical component of service
discovery that prevents using services that are unhealthy.
This page will build upon the previous page and assumes you have a
two node cluster running.
## Defining Checks
Similarly to a service, a check can be registered either by providing a
[check definition](/docs/agent/checks.html)
, or by making the appropriate calls to the
[HTTP API](/docs/agent/http.html).
We will use the check definition, because just like services, definitions
are the most common way to setup checks.
Create two definition files in the Terraform configuration directory of
the second node.
The first file will add a host-level check, and the second will modify the web
service definition to add a service-level check.
```
$ echo '{"check": {"name": "ping", "script": "ping -c1 google.com >/dev/null", "interval": "30s"}}' >/etc/terraform.d/ping.json
$ echo '{"service": {"name": "web", "tags": ["rails"], "port": 80,
"check": {"script": "curl localhost:80 >/dev/null 2>&1", "interval": "10s"}}}' >/etc/terraform.d/web.json
```
The first definition adds a host-level check named "ping". This check runs
on a 30 second interval, invoking `ping -c1 google.com`. If the command
exits with a non-zero exit code, then the node will be flagged unhealthy.
The second command modifies the web service and adds a check that uses
curl every 10 seconds to verify that the web server is running.
Restart the second agent, or send a `SIGHUP` to it. We should now see the
following log lines:
```
==> Starting Terraform agent...
...
[INFO] agent: Synced service 'web'
[INFO] agent: Synced check 'service:web'
[INFO] agent: Synced check 'ping'
[WARN] Check 'service:web' is now critical
```
The first few log lines indicate that the agent has synced the new
definitions. The last line indicates that the check we added for
the `web` service is critical. This is because we're not actually running
a web server and the curl test is failing!
## Checking Health Status
Now that we've added some simple checks, we can use the HTTP API to check
them. First, we can look for any failing checks. You can run this curl
on either node:
```
$ curl http://localhost:8500/v1/health/state/critical
[{"Node":"agent-two","CheckID":"service:web","Name":"Service 'web' check","Status":"critical","Notes":"","ServiceID":"web","ServiceName":"web"}]
```
We can see that there is only a single check in the `critical` state, which is
our `web` service check.
Additionally, we can attempt to query the web service using DNS. Terraform
will not return any results, since the service is unhealthy:
```
dig @127.0.0.1 -p 8600 web.service.terraform
...
;; QUESTION SECTION:
;web.service.terraform. IN A
```
This section should have shown that checks can be easily added. Check definitions
can be updated by changing configuration files and sending a `SIGHUP` to the agent.
Alternatively the HTTP API can be used to add, remove and modify checks dynamically.
The API allows for a "dead man's switch" or [TTL based check](/docs/agent/checks.html).
TTL checks can be used to integrate an application more tightly with Terraform, enabling
business logic to be evaluated as part of passing a check.

View File

@ -6,36 +6,25 @@ sidebar_current: "gettingstarted-install"
# Install Terraform
Terraform must first be installed on every node that will be a member of a
Terraform cluster. To make installation easy, Terraform is distributed as a
[binary package](/downloads.html) for all supported platforms and
architectures. This page will not cover how to compile Terraform from
Terraform must first be installed on your machine. Terraform is distributed
as a [binary package](/downloads.html) for all supported platforms and
architecture. This page will not cover how to compile Terraform from
source.
## Installing Terraform
To install Terraform, find the [appropriate package](/downloads.html) for
your system and download it. Terraform is packaged as a "zip" archive.
your system and download it. Terraform is packaged as a zip archive.
After downloading Terraform, unzip the package. Copy the `terraform` binary to
somewhere on the PATH so that it can be executed. On Unix systems,
`~/bin` and `/usr/local/bin` are common installation directories,
depending on if you want to restrict the install to a single user or
expose it to the entire system. On Windows systems, you can put it wherever
you would like.
### OS X
If you are using [homebrew](http://brew.sh/#install) as a package manager,
than you can install terraform as simple as:
```
brew cask install terraform
```
if you are missing the [cask plugin](http://caskroom.io/) you can install it with:
```
brew install caskroom/cask/brew-cask
```
After downloading Terraform, unzip the package into a directory where
Terraform will be installed. The directory will contain a set of binary
programs, such as `terraform`, `terraform-provider-aws`, etc. The final
step is to make sure the directory you installed Terraform to is on the
PATH. See
[this page](http://stackoverflow.com/questions/14637979/how-to-permanently-set-path-on-linux)
for instructions on setting the PATH on Linux and Mac.
[This page](http://stackoverflow.com/questions/1618280/where-can-i-set-path-to-make-exe-on-windows)
contains instructions for setting the PATH on Windows.
## Verifying the Installation
@ -48,15 +37,13 @@ $ terraform
usage: terraform [--version] [--help] <command> [<args>]
Available commands are:
agent Runs a Terraform agent
force-leave Forces a member of the cluster to enter the "left" state
info Provides debugging information for operators
join Tell Terraform agent to join cluster
keygen Generates a new encryption key
leave Gracefully leaves the Terraform cluster and shuts down
members Lists the members of a Terraform cluster
monitor Stream logs from a Terraform agent
version Prints the Terraform version
apply Builds or changes infrastructure
graph Create a visual graph of Terraform resources
output Read an output from a state file
plan Generate and show an execution plan
refresh Update local state file against real resources
show Inspect Terraform state or plan
version Prints the Terraform version
```
If you get an error that `terraform` could not be found, then your PATH

View File

@ -1,121 +0,0 @@
---
layout: "intro"
page_title: "Terraform Cluster"
sidebar_current: "gettingstarted-join"
---
# Terraform Cluster
By this point, we've started our first agent and registered and queried
one or more services on that agent. This showed how easy it is to use
Terraform, but didn't show how this could be extended to a scalable production
service discovery infrastructure. On this page, we'll create our first
real cluster with multiple members.
When starting a Terraform agent, it begins without knowledge of any other node, and is
an isolated cluster of one. To learn about other cluster members, the agent must
_join_ an existing cluster. To join an existing cluster, only needs to know
about a _single_ existing member. After it joins, the agent will gossip with this
member and quickly discover the other members in the cluster. A Terraform
agent can join any other agent, it doesn't have to be an agent in server mode.
## Starting the Agents
To simulate a more realistic cluster, we are using a two node cluster in
Vagrant. The Vagrantfile can be found in the demo section of the repo
[here](https://github.com/hashicorp/terraform/tree/master/demo/vagrant-cluster).
We start the first agent on our first node and also specify a node name.
The node name must be unique and is how a machine is uniquely identified.
By default it is the hostname of the machine, but we'll manually override it.
We are also providing a bind address. This is the address that Terraform listens on,
and it *must* be accessible by all other nodes in the cluster. The first node
will act as our server in this cluster. We're still not making a cluster
of servers.
```
$ terraform agent -server -bootstrap -data-dir /tmp/consul \
-node=agent-one -bind=172.20.20.10
...
```
Then, in another terminal, start the second agent on the new node.
This time, we set the bind address to match the IP of the second node
as specified in the Vagrantfile. In production, you will generally want
to provide a bind address or interface as well.
```
$ terraform agent -data-dir /tmp/consul -node=agent-two -bind=172.20.20.11
...
```
At this point, you have two Terraform agents running, one server and one client.
The two Terraform agents still don't know anything about each other, and are each part of their own
clusters (of one member). You can verify this by running `terraform members`
against each agent and noting that only one member is a part of each.
## Joining a Cluster
Now, let's tell the first agent to join the second agent by running
the following command in a new terminal:
```
$ terraform join 172.20.20.11
Successfully joined cluster by contacting 1 nodes.
```
You should see some log output in each of the agent logs. If you read
carefully, you'll see that they received join information. If you
run `terraform members` against each agent, you'll see that both agents now
know about each other:
```
$ terraform members
agent-one 172.20.20.10:8301 alive role=terraform,dc=dc1,vsn=1,vsn_min=1,vsn_max=1,port=8300,bootstrap=1
agent-two 172.20.20.11:8301 alive role=node,dc=dc1,vsn=1,vsn_min=1,vsn_max=1
```
<div class="alert alert-block alert-info">
<p><strong>Remember:</strong> To join a cluster, a Terraform agent needs to only
learn about <em>one existing member</em>. After joining the cluster, the
agents gossip with each other to propagate full membership information.
</p>
</div>
In addition to using `terraform join` you can use the `-join` flag on
`terraform agent` to join a cluster as part of starting up the agent.
## Querying Nodes
Just like querying services, Terraform has an API for querying the
nodes themselves. You can do this via the DNS or HTTP API.
For the DNS API, the structure of the names is `NAME.node.terraform` or
`NAME.DATACENTER.node.terraform`. If the datacenter is omitted, Terraform
will only search the local datacenter.
From "agent-one", query "agent-two":
```
$ dig @127.0.0.1 -p 8600 agent-two.node.terraform
...
;; QUESTION SECTION:
;agent-two.node.terraform. IN A
;; ANSWER SECTION:
agent-two.node.terraform. 0 IN A 172.20.20.11
```
The ability to look up nodes in addition to services is incredibly
useful for system administration tasks. For example, knowing the address
of the node to SSH into is as easy as making it part of the Terraform cluster
and querying it.
## Leaving a Cluster
To leave the cluster, you can either gracefully quit an agent (using
`Ctrl-C`) or force kill one of the agents. Gracefully leaving allows
the node to transition into the _left_ state, otherwise other nodes
will detect it as having _failed_. The difference is covered
in more detail [here](/intro/getting-started/agent.html#toc_3).

View File

@ -1,118 +0,0 @@
---
layout: "intro"
page_title: "Key/Value Data"
sidebar_current: "gettingstarted-kv"
---
# Key/Value Data
In addition to providing service discovery and integrated health checking,
Terraform provides an easy to use Key/Value store. This can be used to hold
dynamic configuration, assist in service coordination, build leader election,
and anything else a developer can think to build. The
[HTTP API](/docs/agent/http.html) fully documents the features of the K/V store.
This page assumes you have at least one Terraform agent already running.
## Simple Usage
To demonstrate how simple it is to get started, we will manipulate a few keys
in the K/V store.
Querying the agent we started in a prior page, we can first verify that
there are no existing keys in the k/v store:
```
$ curl -v http://localhost:8500/v1/kv/?recurse
* About to connect() to localhost port 8500 (#0)
* Trying 127.0.0.1... connected
> GET /v1/kv/?recurse HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: localhost:8500
> Accept: */*
>
< HTTP/1.1 404 Not Found
< X-Terraform-Index: 1
< Date: Fri, 11 Apr 2014 02:10:28 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host localhost left intact
* Closing connection #0
```
Since there are no keys, we get a 404 response back.
Now, we can put a few example keys:
```
$ curl -X PUT -d 'test' http://localhost:8500/v1/kv/web/key1
true
$ curl -X PUT -d 'test' http://localhost:8500/v1/kv/web/key2?flags=42
true
$ curl -X PUT -d 'test' http://localhost:8500/v1/kv/web/sub/key3
true
$ curl http://localhost:8500/v1/kv/?recurse
[{"CreateIndex":97,"ModifyIndex":97,"Key":"web/key1","Flags":0,"Value":"dGVzdA=="},
{"CreateIndex":98,"ModifyIndex":98,"Key":"web/key2","Flags":42,"Value":"dGVzdA=="},
{"CreateIndex":99,"ModifyIndex":99,"Key":"web/sub/key3","Flags":0,"Value":"dGVzdA=="}]
```
Here we have created 3 keys, each with the value of "test". Note that the
`Value` field returned is base64 encoded to allow non-UTF8
characters. For the "web/key2" key, we set a `flag` value of 42. All keys
support setting a 64bit integer flag value. This is opaque to Terraform but can
be used by clients for any purpose.
After setting the values, we then issued a GET request to retrieve multiple
keys using the `?recurse` parameter.
You can also fetch a single key just as easily:
```
$ curl http://localhost:8500/v1/kv/web/key1
[{"CreateIndex":97,"ModifyIndex":97,"Key":"web/key1","Flags":0,"Value":"dGVzdA=="}]
```
Deleting keys is simple as well. We can delete a single key by specifying the
full path, or we can recursively delete all keys under a root using "?recurse":
```
$ curl -X DELETE http://localhost:8500/v1/kv/web/sub?recurse
$ curl http://localhost:8500/v1/kv/web?recurse
[{"CreateIndex":97,"ModifyIndex":97,"Key":"web/key1","Flags":0,"Value":"dGVzdA=="},
{"CreateIndex":98,"ModifyIndex":98,"Key":"web/key2","Flags":42,"Value":"dGVzdA=="}]
```
A key can be updated by setting a new value by issuing the same PUT request.
Additionally, Terraform provides a Check-And-Set operation, enabling atomic
key updates. This is done by providing the `?cas=` paramter with the last
`ModifyIndex` value from the GET request. For example, suppose we wanted
to update "web/key1":
```
$ curl -X PUT -d 'newval' http://localhost:8500/v1/kv/web/key1?cas=97
true
$ curl -X PUT -d 'newval' http://localhost:8500/v1/kv/web/key1?cas=97
false
```
In this case, the first CAS update succeeds because the last modify time is 97.
However the second operation fails because the `ModifyIndex` is no longer 97.
We can also make use of the `ModifyIndex` to wait for a key's value to change.
For example, suppose we wanted to wait for key2 to be modified:
```
$ curl "http://localhost:8500/v1/kv/web/key2?index=101&wait=5s"
[{"CreateIndex":98,"ModifyIndex":101,"Key":"web/key2","Flags":42,"Value":"dGVzdA=="}]
```
By providing "?index=" we are asking to wait until the key has a `ModifyIndex` greater
than 101. However the "?wait=5s" parameter restricts the query to at most 5 seconds,
returning the current, unchanged value. This can be used to efficiently wait for
key modifications. Additionally, this same technique can be used to wait for a list
of keys, waiting only until any of the keys has a newer modification time.
This is only a few example of what the API supports. For full documentation, please
reference the [HTTP API](/docs/agent/http.html).

View File

@ -1,139 +0,0 @@
---
layout: "intro"
page_title: "Registering Services"
sidebar_current: "gettingstarted-services"
---
# Registering Services
In the previous page, we ran our first agent, saw the cluster members, and
queried that node. On this page, we'll register our first service and query
that service. We're not yet running a cluster of Terraform agents.
## Defining a Service
A service can be registered either by providing a
[service definition](/docs/agent/services.html),
or by making the appropriate calls to the
[HTTP API](/docs/agent/http.html).
We're going to start by registering a service using a service definition,
since this is the most common way that services are registered. We'll be
building on what we covered in the
[previous page](/intro/getting-started/agent.html).
First, create a directory for Terraform configurations. A good directory
is typically `/etc/terraform.d`. Terraform loads all configuration files in the
configuration directory.
```
$ sudo mkdir /etc/terraform.d
```
Next, we'll write a service definition configuration file. We'll
pretend we have a service named "web" running on port 80. Additionally,
we'll give it some tags, which we can use as additional ways to query
it later.
```
$ echo '{"service": {"name": "web", "tags": ["rails"], "port": 80}}' \
>/etc/terraform.d/web.json
```
Now, restart the agent we're running, providing the configuration directory:
```
$ terraform agent -server -bootstrap -data-dir /tmp/consul -config-dir /etc/consul.d
==> Starting Terraform agent...
...
[INFO] agent: Synced service 'web'
...
```
You'll notice in the output that it "synced" the web service. This means
that it loaded the information from the configuration.
If you wanted to register multiple services, you create multiple service
definition files in the Terraform configuration directory.
## Querying Services
Once the agent is started and the service is synced, we can query that
service using either the DNS or HTTP API.
### DNS API
Let's first query it using the DNS API. For the DNS API, the DNS name
for services is `NAME.service.terraform`. All DNS names are always in the
`terraform` namespace. The `service` subdomain on that tells Terraform we're
querying services, and the `NAME` is the name of the service. For the
web service we registered, that would be `web.service.terraform`:
```
$ dig @127.0.0.1 -p 8600 web.service.terraform
...
;; QUESTION SECTION:
;web.service.terraform. IN A
;; ANSWER SECTION:
web.service.terraform. 0 IN A 172.20.20.11
```
As you can see, an A record was returned with the IP address of the node that
the service is available on. A records can only hold IP addresses. You can
also use the DNS API to retrieve the entire address/port pair using SRV
records:
```
$ dig @127.0.0.1 -p 8600 web.service.terraform SRV
...
;; QUESTION SECTION:
;web.service.terraform. IN SRV
;; ANSWER SECTION:
web.service.terraform. 0 IN SRV 1 1 80 agent-one.node.dc1.consul.
;; ADDITIONAL SECTION:
agent-one.node.dc1.terraform. 0 IN A 172.20.20.11
```
The SRV record returned says that the web service is running on port 80
and exists on the node `agent-one.node.dc1.terraform.`. An additional section
is returned by the DNS with the A record for that node.
Finally, we can also use the DNS API to filter services by tags. The
format for tag-based service queries is `TAG.NAME.service.terraform`. In
the example below, we ask Terraform for all web services with the "rails"
tag. We get a response since we registered our service with that tag.
```
$ dig @127.0.0.1 -p 8600 rails.web.service.terraform
...
;; QUESTION SECTION:
;rails.web.service.terraform. IN A
;; ANSWER SECTION:
rails.web.service.terraform. 0 IN A 172.20.20.11
```
### HTTP API
In addition to the DNS API, the HTTP API can be used to query services:
```
$ curl http://localhost:8500/v1/catalog/service/web
[{"Node":"agent-one","Address":"172.20.20.11","ServiceID":"web","ServiceName":"web","ServiceTags":["rails"],"ServicePort":80}]
```
## Updating Services
Service definitions can be updated by changing configuration files and
sending a `SIGHUP` to the agent. This lets you update services without
any downtime or unavailability to service queries.
Alternatively the HTTP API can be used to add, remove, and modify services
dynamically.

View File

@ -1,56 +0,0 @@
---
layout: "intro"
page_title: "Web UI"
sidebar_current: "gettingstarted-ui"
---
# Terraform Web UI
Terraform comes with support for a
[beautiful, functional web UI](http://demo.terraform.io) out of the box.
This UI can be used for viewing all services and nodes, viewing all
health checks and their current status, and for reading and setting
key/value data. The UI automatically supports multi-datacenter.
For ease of deployment, the UI is
[distributed](/downloads_web_ui.html)
as static HTML and JavaScript.
You don't need a separate web server to run the web UI. The Terraform
agent itself can be configured to serve the UI.
## Screenshot and Demo
You can view a live demo of the Terraform Web UI
[here](http://demo.terraform.io).
While the live demo is able to access data from all datacenters,
we've also setup demo endpoints in the specific datacenters:
[AMS2](http://ams2.demo.terraform.io) (Amsterdam),
[SFO1](http://sfo1.demo.terraform.io) (San Francisco),
and [NYC1](http://nyc1.demo.terraform.io) (New York).
A screenshot of one page of the demo is shown below so you can get an
idea of what the web UI is like. Click the screenshot for the full size.
<div class="center">
<a href="/images/terraform_web_ui.png">
<img src="/images/terraform_web_ui.png">
</a>
</div>
## Set Up
To set up the web UI,
[download the web UI package](/downloads_web_ui.html)
and unzip it to a directory somewhere on the server where the Terraform agent
is also being run. Then, just append the `-ui-dir` to the `terraform agent`
command pointing to the directory where you unzipped the UI (the
directory with the `index.html` file):
```
$ terraform agent -ui-dir /path/to/ui
...
```
The UI is available at the `/ui` path on the same port as the HTTP API.
By default this is `http://localhost:8500/ui`.

View File

@ -13,29 +13,9 @@
<li<%= sidebar_current("vs-other") %>>
<a href="/intro/vs/index.html">Terraform vs. Other Software</a>
<ul class="nav">
<li<%= sidebar_current("vs-other-zk") %>>
<a href="/intro/vs/zookeeper.html">ZooKeeper, doozerd, etcd</a>
</li>
<li<%= sidebar_current("vs-other-chef") %>>
<a href="/intro/vs/chef-puppet.html">Chef, Puppet, etc.</a>
</li>
<li<%= sidebar_current("vs-other-nagios-sensu") %>>
<a href="/intro/vs/nagios-sensu.html">Nagios, Sensu</a>
</li>
<li<%= sidebar_current("vs-other-skydns") %>>
<a href="/intro/vs/skydns.html">SkyDNS</a>
</li>
<li<%= sidebar_current("vs-other-smartstack") %>>
<a href="/intro/vs/smartstack.html">SmartStack</a>
</li>
<li<%= sidebar_current("vs-other-serf") %>>
<a href="/intro/vs/serf.html">Serf</a>
</li>
</li>
<li<%= sidebar_current("vs-other-custom") %>>
<a href="/intro/vs/custom.html">Custom Solutions</a>
@ -50,34 +30,37 @@
<a href="/intro/getting-started/install.html">Install Terraform</a>
</li>
<li<%= sidebar_current("gettingstarted-agent") %>>
<a href="/intro/getting-started/agent.html">Run the Agent</a>
<li<%= sidebar_current("gettingstarted-build") %>>
<a href="/intro/getting-started/build.html">Build Infrastructure</a>
</li>
<li<%= sidebar_current("gettingstarted-services") %>>
<a href="/intro/getting-started/services.html">Services</a>
<li<%= sidebar_current("gettingstarted-change") %>>
<a href="/intro/getting-started/change.html">Change Infrastructure</a>
</li>
<li<%= sidebar_current("gettingstarted-join") %>>
<a href="/intro/getting-started/join.html">Terraform Cluster</a>
<li<%= sidebar_current("gettingstarted-destroy") %>>
<a href="/intro/getting-started/destroy.html">Destroy Infrastructure</a>
</li>
<li<%= sidebar_current("gettingstarted-checks") %>>
<a href="/intro/getting-started/checks.html">Health Checks</a>
<li<%= sidebar_current("gettingstarted-outputs") %>>
<a href="/intro/getting-started/outputs.html">Output Variables</a>
</li>
<li<%= sidebar_current("gettingstarted-kv") %>>
<a href="/intro/getting-started/kv.html">Key/Value Data</a>
<li<%= sidebar_current("gettingstarted-deps") %>>
<a href="/intro/getting-started/dependencies.html">Resource Dependencies</a>
</li>
<li<%= sidebar_current("gettingstarted-ui") %>>
<a href="/intro/getting-started/ui.html">Web UI</a>
<li<%= sidebar_current("gettingstarted-variables") %>>
<a href="/intro/getting-started/variables.html">Input Variables</a>
</li>
<li<%= sidebar_current("gettingstarted-variables") %>>
<a href="/intro/getting-started/provisioners.html">Provision</a>
</li>
<li<%= sidebar_current("gettingstarted-nextsteps") %>>
<a href="/intro/getting-started/next-steps.html">Next Steps</a>
</li>
</ul>
</li>
</ul>