How to Install and Use Gremlin with Mesosphere Marathon

This tutorial will walk you through installing Gremlin on Mesosphere using Marathon Container Orchestration, and testing the functionality with a basic CPU test.

Prerequisites

  • We recommend creating a Gremlin application group in Marathon, and inside of that group creating an application definition for each role in your Marathon configuration. In the case of this guide, we are assuming default roles of private and public.

  • Certificate based authentication should be used, and the certificates should be made available to the Gremlin daemon in /var/lib/gremlin.

  • You will need to create a Marathon Application definition for each Mesosphere role in your cluster. Currently that should be private and public; depending on Mesos version that may be expressed as either agent and agent_public, or slave and slave_public

  • For each Mesosphere role you wish to run Gremlin, the number of instances defined in the application definition should match the number of nodes assigned to the role.

Step 1: Install the Gremlin container into Marathon

This step will need to be repeated for each resource pool you wish to install the Gremlin agent on.

  1. In the Marathon interface, navigate to the group you wish to create the application definition

  2. Click Create Application, located in the upper right hand corner.

  3. In the new application modal, use the radio button on the top right to select JSON mode.

  4. Copy the appropriate JSON definition, located below, into the JSON text field

  5. Click Create Application, located on the lower right hand corner of the modal.

Step 2: Install the Swissknife container into Marathon

The Swissknife container utilizes shellinabox to expose a couple tools to you the user, for the purpose of testing. This container should not be left running past the use of this tutorial, but we have found it to be very helpful in the pursuit of troubleshooting our container environments.

For more information on the Swissknife container, see Docker Hub

  1. In the Marathon interface, navigate to the group you wish to create the application definition

  2. Click Create Application, located in the upper right hand corner.

  3. In the new application modal, use the radio button on the top right to select JSON mode.

  4. Copy the appropriate JSON definition, located below, into the JSON text field

  5. Click Create Application, located on the lower right hand corner of the modal.

Step 3: Open HTOP from the Swissknife container

Let’s get the htop application open to give us real-time metrics to the running swissknife container. As we run an example attack from gremlin this will help us visualize what is happening to the container. The htop application can be found at http://<PUBLIC-NODE-IP>:8888/htop. Also available to you in this container is a root shell located at http://<PUBLIC-NODE-IP>:8888/, a constant ping running to Google http://<PUBLIC-NODE-IP>:8888/gping and a running iostat located at http://<PUBLIC-NODE-IP>:8888/iostat

  1. In the Marathon interface, navigate into swissknife application

  2. Under the running instances find the IP address associated with the container

    1. Depending on your network security settings, you may need to open access to port 8888
    2. The JSON definition assumes the role of slave_public, you may need to change this if that is not the public role you use
    3. If the IP address given my Mesosphere is the private IP of the node the container is on, you will need to get the public IP of the node
  3. In a new brower window, open the above IP to http://<PUBLIC-NODE-IP>:8888/htop

Congratulations, you should now see the htop interface in your web browser. Leave this open, as we’ll be referring back to it in the next step.

Step 4: Run a test attack

  1. In a new browser window, open the link to the Gremlin Attacks UI: https://app.gremlin.com/attacks

  2. Click New Attack

  3. Select the Containers tab

  4. Select the swissknife container we created as your target by clicking the checkbox next to the container ID, you should be able to find this based on the Docker key-value pair we added, app:swissknife

  5. Click Choose a Gremlin

  6. Select the Resource category and CPU attack

  7. Default values should be fine, click Unleash Gremlin to launch the attack

  8. Observe in the open htop browser window that you can see the increased CPU load on the docker container.

Conclusion

You now have Gremlin up and running in your ECS environment, and validated it’s functionality against a running swissknife container. For security, you should remove the swissknife container from your running cluster, as it’s an unsecured metric view into your running environment.

Feel free to expand this to other Mesosphere environments and have fun running Chaos Experiments!

JSON Application Definitions

  • The Gremlin application definitions map the mount points /var/run/docker.sock, /var/log/gremlin and /var/lib/gremlin to the same locations on the host nodes

  • The GREMLIN_TEAM_CERTIFICATE_OR_FILE, GREMLIN_TEAM_PRIVATE_KEY_OR_FILE and GREMLIN_CLIENT_TAGS fields will need to be updated to fit with your environment.

  • As stated above, you will need to update the number of instances to match the number of nodes in each resource pool.

  • If you are using overlapping resource pools, additional consideration will need to ensure an even non-overlapping deployment of Gremlin, as multiple agent containers running on a single host is not recommended.

Gremlin Private JSON Definition

{
  "id": "/gremlin/gremlin-agent",
  "cmd": "/entrypoint.sh daemon",
  "cpus": 0.25,
  "mem": 64,
  "disk": 0,
  "instances": 2,
  "constraints": [
    [
      "hostname",
      "UNIQUE"
    ]
  ],
  "acceptedResourceRoles": [
    "*"
  ],
  "container": {
    "type": "DOCKER",
    "docker": {
      "forcePullImage": true,
      "image": "gremlin/gremlin",
      "parameters": [
        {
          "key": "cap-add",
          "value": "NET_ADMIN"
        },
        {
          "key": "cap-add",
          "value": "SYS_BOOT"
        },
        {
          "key": "cap-add",
          "value": "SYS_TIME"
        },
        {
          "key": "cap-add",
          "value": "KILL"
        }
      ],
      "privileged": true
    },
    "volumes": [
      {
        "containerPath": "/var/run/docker.sock",
        "hostPath": "/var/run/docker.sock",
        "mode": "RW"
      },
      {
        "containerPath": "/var/log/gremlin",
        "hostPath": "/var/log/gremlin",
        "mode": "RW"
      },
      {
        "containerPath": "/var/lib/gremlin",
        "hostPath": "/var/lib/gremlin",
        "mode": "RW"
      }
    ]
  },
  "env": {
    "GREMLIN_TEAM_CERTIFICATE_OR_FILE": "file:///var/lib/gremlin/team_pub.pem",
    "GREMLIN_TEAM_PRIVATE_KEY_OR_FILE": "file:///var/lib/gremlin/team_priv.pem",
    "GREMLIN_CLIENT_TAGS": "mesoscluster=<Your_Mesos_Cluster_Name>,owner=<Your_Name>,mesosrole=private"
  },
  "labels": {
    "name": "gremlin"
  },
  "portDefinitions": [
    {
      "port": 10000,
      "protocol": "tcp"
    }
  ]
}

Gremlin Public JSON Definition

{
  "id": "/gremlin/gremlin-agent-public",
  "cmd": "/entrypoint.sh daemon",
  "cpus": 0.25,
  "mem": 64,
  "disk": 0,
  "instances": 2,
  "constraints": [
    [
      "hostname",
      "UNIQUE"
    ]
  ],
  "acceptedResourceRoles": [
    "slave_public"
  ],
  "container": {
    "type": "DOCKER",
    "docker": {
      "forcePullImage": true,
      "image": "gremlin/gremlin",
      "parameters": [
        {
          "key": "cap-add",
          "value": "NET_ADMIN"
        },
        {
          "key": "cap-add",
          "value": "SYS_BOOT"
        },
        {
          "key": "cap-add",
          "value": "SYS_TIME"
        },
        {
          "key": "cap-add",
          "value": "KILL"
        }
      ],
      "privileged": true
    },
    "volumes": [
      {
        "containerPath": "/var/run/docker.sock",
        "hostPath": "/var/run/docker.sock",
        "mode": "RW"
      },
      {
        "containerPath": "/var/log/gremlin",
        "hostPath": "/var/log/gremlin",
        "mode": "RW"
      },
      {
        "containerPath": "/var/lib/gremlin",
        "hostPath": "/var/lib/gremlin",
        "mode": "RW"
      }
    ]
  },
  "env": {
    "GREMLIN_TEAM_CERTIFICATE_OR_FILE": "file:///var/lib/gremlin/team_pub.pem",
    "GREMLIN_TEAM_PRIVATE_KEY_OR_FILE": "file:///var/lib/gremlin/team_priv.pem",
    "GREMLIN_CLIENT_TAGS": "mesoscluster=<Your_Mesos_Cluster_Name>,owner=<Your_Name>,mesosrole=public"
  },
  "labels": {
    "name": "gremlin"
  },
  "portDefinitions": [
    {
      "port": 10000,
      "protocol": "tcp"
    }
  ]
}

Swissknife JSON definition

{
  "id": "/swissknife",
  "cmd": null,
  "cpus": 0.25,
  "mem": 64,
  "disk": 0,
  "instances": 1,
  "constraints": [
    [
      "hostname",
      "UNIQUE"
    ]
  ],
  "acceptedResourceRoles": [
    "slave_public"
  ],
  "container": {
    "type": "DOCKER",
    "docker": {
      "forcePullImage": true,
      "image": "khultman/swissknife",
      "parameters": [],
      "privileged": false
    },
    "volumes": [],
    "portMappings": [
      {
        "containerPort": 8888,
        "hostPort": 8888,
        "labels": {},
        "name": "default",
        "protocol": "tcp",
        "servicePort": 8888
      }
    ]
  },
  "labels": {
    "app": "swissknife"
  },
  "networks": [
    {
      "mode": "container/bridge"
    }
  ],
  "portDefinitions": []
}