Create a Digital Ocean droplet with Terraform

Infrastructure as code

I’ve been meaning to try out terraform. It gives you the power to define your infrastructure with code. It plugs in with all major cloud providers. Here’s some links below:

https://www.terraform.io/
https://www.terraform.io/docs/providers/index.html

For simplicity sake I played around with terraform and digitalocean.
https://www.terraform.io/docs/providers/do/index.html

A couple things you have to just setup on digital ocean:

  • Add your ssh key to digital ocean – copy the name of your ssh key, paste it somewhere for reuse
  • Create a digital ocean api token – copy the token, paste it somewhere for reuse

Install terraform and make sure you can run the “terraform” command. (On mac, I had to move the install to /usr/local/bin/ ) https://www.terraform.io/intro/getting-started/install.html

Check terraform can be run correctly:

1
$ terraform -v

Setup main.tf file

I’ve put together this script, save it as main.tf in a new folder

Setup environment variables and run commands

You need to set up 2 environment variables. Use the two copied values from above:

1
2
export DOTOKEN="YOUR_DIGITAL_OCEAN_API_TOKEN_HERE"
export SSHKEYNAME="SSH_KEY_NAME_FROM_DIGITAL_OCEAN"

Test the values are correct:

1
2
echo $DOTOKEN
echo $SSHKEYNAME

Run the following commands:

1
2
3
4
$ terraform init
$ terraform plan -var="do_token=$DOTOKEN" -var="ssh_key_name=$SSHKEYNAME"
   - Output will end with: Plan: 1 to add, 0 to change, 0 to destroy.
$ terraform apply -var="do_token=$DOTOKEN" -var="ssh_key_name=$SSHKEYNAME"

If you get any authentication errors, make sure you have set up your ssh key with your computer’s public key.

After the “apply” command you’ll see an ip.

1
2
3
Outputs:

controller_ip_address = 127.0.0.1

(127.0.0.1 is just a placeholder ip value, you’ll get a different value which you can use)

ssh into the new server

Then, because we supplied the ssh key name, the new server will have our ssh key set up already.

You can ssh into the server using the ip displayed:

You now have an ubuntu droplet to play around with. When you’re done, just run the “destroy” command below:

1
$ terraform destroy -var="do_token=$DOTOKEN" -var="ssh_key_name=$SSHKEYNAME"

You can do some great things with terraform. You can spin up multiple servers to practice distributed systems. You can add chef into the mix and make sure the right software is setup on the servers in preparation for code.

You might then want to install a container orchestrator like kubernetes or swarm, then run yaml scripts. And automate it all via ansible, jenkins, gitlab etc. The possibilities are endless 🙂

Keep swimming…

Here’s a link for using chef with terraform:

https://www.terraform.io/docs/provisioners/chef.html

Kubernetes and Kong with a Kong Dashboard local

I quickly threw this together just to see if I could get it working on my local machine using docker for mac and kubernetes.

It’s pretty rough, but just putting it here in case anyone needs the same info I pulled together.

This is for local testing with NodePort, not for production or cloud use.
I also used postgres.

Kong kubernetes setup documentation here:

https://docs.konghq.com/install/kubernetes/

Steps to set up kong locally using kubernetes and docker for mac

Enable kubernetes with docker for mac

  • Click on docker preferences
  • Click on the Kubernetes tab
  • Select enable kubernetes checkbox and click on the kubernetes radio button

Note: Make sure kubernetes has access to internet, if it does not start up, check internet connection. If you run on a VPN that has strict security firewalls, that might be preventing kubernetes from installing.

Update type to NodePort

In order for kong to run locally you need to update the type from LoadBalancer to NodePort.

Also make sure the kong version you are using is supported by the kong dashboard image. At the time of writing this only kong version under 0.14 are supported. So I updated the version of kong to 0.13 in the yaml scripts.

Updated kong tag to 0.13

Yaml files

Grab the yaml files from here:

https://github.com/CariZa/kubernetes-kong-with-dashboard

Commands:

1
2
3
4
5
kubectl create -f postgres.yaml    

kubectl create -f kong_postgres.yaml

kubectl create -f kong_migration_postgres.yaml

Check the service ip for

1
kubectl get svc

Copy the ip of the kong-admin service and paste it in kong_dashboard.yml as an “args” value, eg:

When you run “$ kubectl get service” you might get this response:

1
2
3
4
    NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
    ...
    kong-admin         NodePort    10.101.71.20     <none>        8001:30916/TCP   46m
    ...

What you want to take is the CLUSTER-IP and the first part of the PORT(S)

1
10.97.55.180:8001

You will add it in the kong_dashboard.yaml file by the args list around line 34:

1
args: ["start", "--kong-url", "http://10.101.71.20:8001"]

Then create the kong-dashboard:

1
kubectl create -f kong_dashboard.yml

To check if your dashboard runs correctly check the logs.

First get the full pod name for kong-dashboard:

1
kubectl get pods

It will be something like **kong-dashboard-86dfddcfdf-qgnhl**

Then check the logs:

1
kubectl logs [pod-name]

eg

1
kubectl logs kong-dashboard-86dfddcfdf-qgnhl

You should see

1
2
3
4
5
    Connecting to Kong on http://10.101.71.20:8001 ...
    Connected to Kong on http://10.101.71.20:8001.
    Kong version is 0.13.1
    Starting Kong Dashboard on port 8080
    Kong Dashboard has started on port 8080

If you only see

1
    Connecting to Kong on http://10.101.71.20:8001

It might still be starting up or your internal kong-admin url could be incorrect. Remember the url is the kubernetes internal url.

Test the dashboard works

You should be able to access your kong-dashboard using the service port:

1
kubectl get service

Grab the port by the kong-dashboard service, it will be the second port:

1
2
3
4
NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
...
kong-dashboard     NodePort    10.97.55.180     <none>        8080:30719/TCP   1h
...

In this case the port value is 30719

So the url will be:

http://localhost:30719

Note

This is for local testing with NodePort, not for production or cloud use.

Screenshots

This is what I a see on my side at the date of publication:

I added a test api entry, pointed to a service I was running on kubernetes:

This is the settings I added for the test:

I got the url but checking the service value:

1
kubernetes get service

I get the values:

1
hello-kubernetes   NodePort    10.106.125.184   <none>        8080:30281/TCP   22h

I used “10.106.125.184” and “8080” as the “upsteam_url”

And I could then access the internal route using the kong-proxy ip and the path I set “/test”

Eg:

http://localhost:32246/test

localhost:32246 -> Kong Proxy url
/test -> The path I told the kong_dashboard to use and redirect to the internal url “10.106.125.184:8080”

Elastic beats tutorial with docker

Elastic beats tutorial: A quick look at using elastic beats with docker containers

Creating a POC of elastic beats

I gave myself a single goal today: Get a working POC of elastic beats using an nginx container and a beats container. I wanted specifically to prove that I could create a loosely coupled container logging system using beats containers.

Here’s a quick overview of the POC:

Just a quick note, the files below are very particular about spaces/indentation used. If you get strange errors just check the spaces are correct. WordPress keeps removing my spaces, I think I’ve added them in now permanently, just make sure the right spacing and indentation is in your files.

Step 1: Beats

Setup the configuration of the metricbeat.

Create a new directory for you to save your configuration file:

1
$ mkdir practice-beats

Then create a file called “metricbeat.yml” and add this:

1
2
3
4
5
6
7
8
9
10
11
12
13
metricbeat.modules:
- module
: nginx
  metricsets
: ["stubstatus"]
  period
: 10s

  # Nginx hosts
  hosts
: ["my-awesome-nginx"]

  # Path to server status. Default server-status
  #server_status_path: "server-status"

output.console
:
  pretty
: true

The important parts are that we are using the nginx elastic beats module. And we are going to be watching for the my-awesome-nginx host (which we will create in step 2).

The other important part to note is we are mapping the output to the console:

1
2
output.console:
pretty: true

I’m mapping to the console/terminal for simplicity but you can map the output to something more useful like logstash or elasticsearch (I’ll do follow up posts on that process).

Run the following docker command to create the beats container:

1
2
3
docker run --name my-awesome-beats \
-v $(pwd)/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml \
docker.elastic.co/beats/metricbeat:6.2.4

Beats will trigger the metricbeat request to nginx every 10 seconds.

You will see data like this appearing every 10 seconds:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"@timestamp": "2018-06-08T14:51:20.367Z",
"@metadata": {
"beat": "metricbeat",
"type": "doc",
"version": "6.2.4"
},
"metricset": {
"host": "my-awesome-nginx",
"rtt": 11910,
"name": "stubstatus",
"module": "nginx"
},
"error": {
"message": "error making http request: Get http://my-awesome-nginx/server-status: lookup my-awesome-nginx on 192.168.65.1:53: no such host"
},
"beat": {
"name": "7e29608de529",
"hostname": "7e29608de529",
"version": "6.2.4"
}
}

For now you will see this error:

1
WARN transport/tcp.go:36 DNS lookup failure "my-awesome-nginx": lookup my-awesome-nginx on 192.168.65.1:53: no such host

That’s ok, we just need to setup a custom network, which we’ll do in Step 3.

What you are seeing now is a working beat 🙂 It’s alive!

Step 2: Nginx

Lets create a nginx container with some configurations as well.

Leave the elastic beats terminal running, open a new terminal window and follow the following steps:

We need to edit the default nginx configuration and add an nginx module called “stubstatus”.

Useful links:

https://www.tecmint.com/enable-nginx-status-page/

https://nginx.org/en/docs/http/ngx_http_stub_status_module.html

(Just a note, in the above examples, the location for metricbeats needs to be “location /server-status”)

To do this we add this to the default.conf file.

Go out of your current directory and then create a new directory:

1
$ mkdir practice-nginx

Then in there create 2 files:
– default.conf
– index.html

The metricbeats module will be triggering on this endpoint: “/server-status” so we need to make sure that nginx puts the right information at the point by adding this:

1
2
3
4
5
...
location /server-status {
stub_status;
}
...

Copy and paste this configuration script to your default.conf file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    location /server-status {
        stub_status;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}

Let’s create an index.html page with only the text “Hello world!”.

So if you are using a terminal you can just do this:

1
2
$ vi index.html
hello world

And save 🙂

Then run the following docker command to spin up an nginx container:

1
2
3
4
docker run --name my-awesome-nginx \
-v $(pwd)/index.html:/usr/share/nginx/html/index.html \
-v $(pwd)/default.conf:/etc/nginx/conf.d/default.conf \
-p 8010:80 -d nginx

What are we doing here?

–name lets us give the container we are about to spin up a name
-v here we are voluming a file, we use it twice to volume two files, index.html and default.conf
index.html is going in a location where nginx is expecting to find an index.html file
Inside the container it will be looking in the location /usr/share/nginx/
default.conf is the configuration file that nginx will use by default. it is located in the path /etc/nginx/conf.d/
-p here we are telling docker to map the port 8010 on our computer to the port 80 on the nginx container

If the docker command ran correctly you should be able to go to this url and see our hello world:

1
http://localhost:8010

You should see your Hello World!

We should also be able to check that our configuration change works by going here:

1
http://localhost:8010/server-status

You should see something like:

1
2
3
4
Active connections: 1
server accepts handled requests
133 133 191
Reading: 0 Writing: 1 Waiting: 0

Step 3: Network Beats container and the Nginx container

Next we need to create a custom user network. Let’s call it “catsandboots”.

There’s a little easter egg joke in that 😉 Hint: Ask Siri to beatbox.

To create our docker network we can run this command:

1
$ docker network create catsandboots

To add our nginx and beats containers to that network we can do this:

We named the two containers like this:
– my-awesome-beats
– my-awesome-nginx

We then need to run this docker command:

1
2
$ docker network connect catsandboots my-awesome-beats
$ docker network connect catsandboots my-awesome-nginx

To check they’re on the right network you can run:

1
$ docker inspect my-awesome-beats

and

1
$ docker inspect my-awesome-nginx

Alternatively you can inspect the network and go:

1
$ docker inspect catsandboots

If you go back to the terminal with the running elastic beats container (aka my-awesome-beats) then you should now start seeing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"@timestamp": "2018-06-08T15:07:20.385Z",
"@metadata": {
"beat": "metricbeat",
"type": "doc",
"version": "6.2.4"
},
"metricset": {
"host": "my-awesome-nginx",
"rtt": 4574,
"name": "stubstatus",
"module": "nginx"
},
"nginx": {
"stubstatus": {
"current": 3,
"dropped": 0,
"writing": 1,
"waiting": 0,
"accepts": 2,
"requests": 3,
"hostname": "my-awesome-nginx",
"reading": 0,
"handled": 2,
"active": 1
}
},
"beat": {
"name": "7e29608de529",
"hostname": "7e29608de529",
"version": "6.2.4"
}
}

Your beats container is now receiving actual data from nginx via the nginx substatus module. Just in case you didn’t see this useful link above here it is again: https://nginx.org/en/docs/http/ngx_http_stub_status_module.html

You should now be seeing useful information like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
"nginx": {
"stubstatus": {
"current": 3,
"dropped": 0,
"writing": 1,
"waiting": 0,
"accepts": 2,
"requests": 3,
"hostname": "my-awesome-nginx",
"reading": 0,
"handled": 2,
"active": 1
}

Done!

So that’s a quick tutorial to get an instance of elastic beats metricbeat running as a POC. There are a bunch of beats you can use, check out the documentation. I’m gonna try get a couple more tutorials out for some of the other beats. And also get a proper working elastic stack setup from container to beats to elasticsearch to kibana 🙂 Soon.