Monitoring Elixir Applications
monitoring
5
elixir
37
White

Trần Việt Thắng viết ngày 04/03/2019

Bạn đang viết application với Elixir? Bạn sắp release sản phẩm hay đơn giản thỉnh thoảng bạn không biết tại sao service A lại lăn đùng ra chết hay số message tồn trong queue tại tăng chóng mặt. Thì đó là lúc bạn cần monitoring. Làm sao để biết được toàn cảnh hành vi hay perfomance của hệ thống theo thời gian.

Việc monitoring trong Elixir application cũng có một lợi thế nữa là bạn có thể sử dụng khá nhiều tools từ Erlang đã có sẵn trước đó để áp dụng vào hệ thống của mình.

Stack

Có rất nhiều stack hiện nay có thể áp dụng cho nhiều loại hệ thống khác nhau. Nhưng với nhu cầu hiện tại khá đơn giản của tui thì tui đang sử dụng Influxdb cho time-series database, Telegraf làm agent cho việc pull metrics và Grafana cho việc visualizing metrics.

stack

Bạn thấy về cơ bản là khá đơn giản đúng không?

Time-series database

Việc sử dụng time-series database là thực sự cần thiết cho việc storing metrics hay những gì mà bạn muốn track theo thời gian. Còn lý do tại sao tui chọn InfluxDB thì do tui có hype đó. Hihi. Cái nào hot thì xài thôi :)) Thực ra stack này tôi có tham khảo TICK stack mà anh Quần Cam đã suggest.

Về lợi thế thì InfluxDB có những gì?

  • Timestamps optimization
  • High writes, high query loads
  • Strong SQL-like query language
  • Index theo tags, rất nhanh và thuận tiện cho việc query
  • Cung cấp api đơn giản thông qua http hoặc udp hoặc giao thức riêng của InfluxDB - line protocol
  • Vân vân và mây mây mình cũng chưa khám phá hết :)

Bạn có thể đọc thêm documentations của InfluxDB để hiểu rõ hơn về time-series database này.

Collecting and Reporting metrics

Bạn đã có database rồi. Giờ sao. Làm sao để collect metrics? Ở đây tui sử dụng Telegraf để pull metrics từ các services. Telegraf là một pluginable agent có thể collect inputs từ rất nhiều nguồn khác nhau (K có đếm cơ mà đâu đó tầm 150 sources) và gửi đến các outputs mà bạn có thể chỉ định. Ví dụ output ở đây có thể là InfluxDB.

[agent]
  interval = "10s"
  round_interval = true
  metric_batch_size = 1000
  metric_buffer_limit = 10000
  collection_jitter = "0s"
  flush_interval = "10s"
  flush_jitter = "0s"
  precision = ""
  debug = false
  quiet = false
  hostname = ""
  omit_hostname = false

[[outputs.influxdb]]
  urls = ["http://influxdb:8086"]
  database = "telegraf"
  retention_policy = ""
  write_consistency = "any"
  timeout = "5s"

# Read metrics about cpu usage
[[inputs.cpu]]

# Read metrics about disk usage by mount point
[[inputs.disk]]

# Read metrics about disk IO by device
[[inputs.diskio]]

# Get kernel statistics from /proc/stat
[[inputs.kernel]]

# Read metrics about memory usage
[[inputs.mem]]

# Get the number of processes and group them by status
[[inputs.processes]]

# Read metrics about swap memory usage
[[inputs.swap]]

# Read metrics about system load & uptime
[[inputs.system]]

[[inputs.socket_listener]]
  service_address = "udp://:8092"
  data_format = "influx"

[[inputs.rabbitmq]]
   url = "http://rabbitmq-1:15672"
   name = "rmq-server-1" # optional tag
   username = "$R_USERNAME"
   password = "$R_PASSWORD"

Trên đây là một config cơ bản giúp bạn có thể pull metrics từ các nhiều nguồn khác nhau. Gởi các thông tin như mem, disk, cpu, rabbitmq overview,... tới outputs theo khoảng thời gian interval mà bạn có thể đặt trước. Điều này giúp bạn có thể tránh việc network traffic tăng cao hay việc writes data xuống InfluxDB quá nhiều trong một thời điểm. Vì vậy bạn có thể đặt Telegraf đứng giữa Application và database trong trường hợp bạn collect metrics từ app với số lượng lớn một cách liên tục. Trong trường hợp bạn muốn kéo metrics từ nhiều services khác nhau, bạn nên chạy nhiều telegraf daemons khác nhau để có thể cài đặt inputs-outputs cho phù hợp.

Elixir application metrics

Driver

Về driver cho InfluxDB thì tui có sử dụng Fluxter giúp tạo connections pool tới InfluxDB/Telegraf. Api thì cũng rất đơn giản.

Đầu tiên bạn cần define module sẽ sử dụng fluxter behavior:

defmodule MyApp.Fluxter do
  use Fluxter
end
def start(_type, _args) do
  children = [
    MyApp.Fluxter.child_spec(),
    # ...
  ]

  Supervisor.start_link(children, strategy: :one_for_one)
end

Config để kết nối tới metrics collector

config :fluxter, MyApp.Fluxter,
  host: System.get_env("TELEGRAF_HOST") || "web-telegraf",
  port: String.to_integer(System.get_env("TELEGRAF_PORT") || "8092"),
  pool_size: 10

Xong! Giờ thì bạn có thể report metrics với Fluxter

def do_something() do
  MyApp.Fluxter.write("something_done", [func: "do_something"], 1)
  Myapp.Fluxter.measure("func_exec_time", [func: "another_func"], &another_func/0)
end

Ví dụ bạn có thể collect request time của tất cả các request tới application:

defmodule MyApp.Plug.EuMetrics do
  @time_unit :milli_seconds

  def init(opts), do: opts

  def call(conn, _) do
    start_time = :erlang.monotonic_time(@time_unit)

    Plug.Conn.register_before_send(conn, fn conn ->
      end_time = :erlang.monotonic_time(@time_unit)

      MyApp.Fluxter.write(
        "request_time",
        [type: "render_req"],
        value: (end_time - start_time) / 1
      )

      conn
    end)
  end
end

hay measuring Ecto

defmodule MyApp.Repo do  
  use Ecto.Repo, otp_app: :myapp

  def log(entry) do
    MyApp.Fluxter.write(
      "query_exec_time",
      [],
      (entry.query_time + entry.queue_time || 0) / 1000
    )

    super(entry)
  end
end  

Erlang VM stats

vmstats là một erlang application tổng hợp metrics từ erlang vm rồi gửi chúng tới một configurable sink

config :vmstats,
  sink: MyApp.Fluxter,
  sched_time: true,
  base_key: "vmstats",
  key_seperator: ".",
  # in ms
  interval: 1000,
  memory_metrics: [
    {:total, :total},
    {:processes_used, :procs_used},
    {:atom_used, :atom_used},
    {:binary, :binary},
    {:ets, :ets}
  ]

vmstats yêu cầu chúng ta phải implement vmstats_sink behavior và may thay Fluxter pool có thể được sử dụng như là một vmstats sink

 defmodule MyApp.Fluxter do
  use Fluxter

  @behaviour :vmstats_sink

  def collect(_type, name, value) do
    write(name, value: value)
  end
end

Ok vậy là cài đặt xong, vmtats giúp chúng ta collect những loại metrics gì? Nào là memory used cho ETS tables, atoms, processes, binaries và total memory, number of process, process limit, garbage collection count,.. nói chung là tả phí lù thứ chắc là bạn sẽ cần :)))

Visualizing metrics

Việc hiện thị metrics thì tui sử dụng Grafana. Grafana là 1 open-source có native support cho InfluxDB đồng nghĩa với việc nó sẽ support tận răng cho các bạn. Sau tất cả chúng ta có thể có một dashboard kiểu như thế này:

alt text

Như tui đã nói. Grafana cung cấp một giao diện rất đơn giản support tận răng cho nhiều kiểu dữ liệu trong đó có influx. Bạn chỉ cần cài đặt datasource đúng kiểu rồi có thể query data rất dễ dàng

alt text

Alerting

Tất nhiên không phải lúc nào bạn cũng phải dán mắt vào màn hình dashboard cả ngày được đúng không. Hoặc đang đêm server nổ cái toang mà bạn không hề hay biết. Lúc này alerting mới thực sự quan trọng.

Thực ra trọng bộ TICK stack còn có 1 component tên là Kapacitor - Realtime data process streaming có nhiệm vụ process data và trigger action dựa trên kết quả trả về. (VD như Kapacitor sẽ kéo cpu measurement về nếu như quá ngưỡng cho phép thì trigger alert). Nhưng hiện tại tui chưa có dùng Kapacitor mà dùng luôn tính năng alerting của Grafana.

alt text

Như vậy là tui có thể bật notification channel alerting trên slack rồi yên tâm nằm ngủ rồi. Hehe

Tổng kết

Sau khi nhận thấy application của mình ngày càng khó quản lý bằng cơm. Cuối cùng tui cũng phải build một hệ thống monitoring riêng cho mình. Tuy đơn giản nhưng hiệu quả mang lại là rất cao. Cũng vì muốn các bạn chưa từng build một monitoring service nào có thể có một chút view về vấn đề này. Nên tui viết bài này với hy vọng những bạn đang mò mẫm như tui lúc mới bắt đầu làm có thể có những solutions cho riêng mình.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Trần Việt Thắng

1 bài viết.
1 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Bài viết liên quan
White
9 6
Chưa xem phần 2? Xem (Link) Trong bài viết này tôi giới thiệu cho các bạn về khái niệm function arity, một cách gọi mĩ miều của số lượng argument ...
Lơi Rệ viết 4 năm trước
9 6
White
3 2
Source code: https://github.com/yuen26/springbootprometheus 1. TÌm hiểu nhanh về Prometheus 1.1. Prometheus là gì? (Link) là một opensource ...
Nguyễn Tuấn Anh viết 4 tháng trước
3 2
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
1 bài viết.
1 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!