Zlogs

Introduction

Zlogs is a utility that allows you to stream VM logs to a remote location. You can find the full description here

Using Zlogs

In Terraform, a vm has a zlogs field, this field should contain a list of target URLs to stream logs to.

Valid protocols are: ws, wss, and redis.

For example, to deploy two VMs named "vm1" and "vm2", with one vm1 streaming logs to vm2, this is what main.tf looks like:

terraform {
  required_providers {
    grid = {
      source = "threefoldtech/grid"
    }
  }
}

provider "grid" {
}

resource "grid_network" "net1" {
    nodes = [2, 4]
    ip_range = "10.1.0.0/16"
    name = "network"
    description = "some network description"
    add_wg_access = true
}

resource "grid_deployment" "d1" {
  node = 2
  network_name = grid_network.net1.name
  ip_range = lookup(grid_network.net1.nodes_ip_range, 2, "")
  vms {
    name = "vm1" #streaming logs
    flist = "https://hub.grid.tf/tf-official-apps/base:latest.flist"
    entrypoint = "/sbin/zinit init"
    cpu = 2
    memory = 1024
    env_vars = {
      SSH_KEY = "PUT YOUR SSH KEY HERE"
    }
    zlogs = tolist([
      format("ws://%s:5000", replace(grid_deployment.d2.vms[0].computedip, "//.*/", "")),  
    ])
  }
}

resource "grid_deployment" "d2" {
  node = 4
  network_name = grid_network.net1.name
  ip_range = lookup(grid_network.net1.nodes_ip_range, 4, "")
  vms {
    name = "vm2" #receiving logs
    flist = "https://hub.grid.tf/tf-official-apps/base:latest.flist"
    cpu = 2 
    memory = 1024
    entrypoint = "/sbin/zinit init"
    env_vars = {
      SSH_KEY = "PUT YOUR SSH KEY HERE"
    }
    publicip = true
  }
}

At this point, two VMs are deployed, and vm1 is ready to stream logs to vm2. But what is missing here is that vm1 is not actually producing any logs, and vm2 is not listening for incoming messages.

Creating a server

  • First, we will create a server on vm2. This should be a websocket server listening on port 5000 as per our zlogs definition in main.tf ws://%s:5000.

  • a simple python websocket server looks like this:

import asyncio
import websockets
import gzip


async def echo(websocket):
    async for message in websocket:
        data = gzip.decompress(message).decode('utf-8')
        f = open("output.txt", "a")
        f.write(data)
        f.close()

async def main():
    async with websockets.serve(echo, "0.0.0.0", 5000, ping_interval=None):
        await asyncio.Future()

asyncio.run(main())
  • Note that incoming messages are decompressed since zlogs compresses any messages using gzip.
  • After a message is decompressed, it is then appended to output.txt.

Streaming logs

  • Zlogs streams anything written to stdout of the zinit process on a vm.
  • So, simply running echo "to be streamed" 1>/proc/1/fd/1 on vm1 should successfuly stream this message to the vm2 and we should be able to see it in output.txt.
  • Also, if we want to stream a service's logs, a service definition file should be created in /etc/zinit/example.yaml on vm1 and should look like this:
exec: sh -c "echo 'to be streamed'"
log: stdout
Last change: 2024-10-29