 
Numa discussão no fediverso falamos sobre habilitar as métricas do GoToSo, também conhecido como GoToSocial. Fiz algumas mudanças e consegui expor essas métricas.
  O compose.yaml do GoToSo:
  
services:
  gotosocial:
    image: docker.io/superseriousbusiness/gotosocial:latest
    container_name: gotosocial
    user: 1000:1000
    networks:
      - gotosocial
    environment:
      GTS_HOST: bolha.linux-br.org
      GTS_DB_TYPE: postgres
      GTS_CONFIG_PATH: /gotosocial/config.yaml
      [...]
      OTEL_METRICS_PRODUCERS: prometheus
      OTEL_METRICS_EXPORTER: prometheus
      OTEL_EXPORTER_PROMETHEUS_HOST: 0.0.0.0
      OTEL_EXPORTER_PROMETHEUS_PORT: 9090
      [...]
    ports:
      - "8080:8080"
      - "9090:9090"
    [...]
  
  A config.yaml também do GoToSo:
  
[...]
media-emoji-local-max-size: 250KiB
media-emoji-remote-max-size: 250KiB
advanced-rate-limit-requests: 0
metrics-enabled: true
  
Uma vez que isso estava habilitado e o container reiniciado, foi só verificar a porta 9090.
  
❯ curl -s localhost:9090/metrics | head -10
# HELP go_config_gogc_percent Heap size target percentage configured by the user, otherwise 100.
# TYPE go_config_gogc_percent gauge
go_config_gogc_percent{otel_scope_name="go.opentelemetry.io/contrib/instrumentation/runtime",otel_scope_schema_url="",otel_scope_version="0.63.0"} 100
# HELP go_goroutine_count Count of live goroutines.
# TYPE go_goroutine_count gauge
go_goroutine_count{otel_scope_name="go.opentelemetry.io/contrib/instrumentation/runtime",otel_scope_schema_url="",otel_scope_version="0.63.0"} 167
# HELP go_memory_allocated_bytes_total Memory allocated to the heap by the application.
# TYPE go_memory_allocated_bytes_total counter
go_memory_allocated_bytes_total{otel_scope_name="go.opentelemetry.io/contrib/instrumentation/runtime",otel_scope_schema_url="",otel_scope_version="0.63.0"} 1.6433066284e+11
# HELP go_memory_allocations_total Count of allocations to the heap by the application.    
  
Em seguida subi um container, também com podman, pra coletar esses dados. Junto com um prometheus-exporter pra coletar dados da máquina.
compose.yaml:
  
services:
  prometheus:
    image: quay.io/prometheus/prometheus
    container_name: prometheus
    environment:
      TZ: Europe/Stockholm
    ports:
      - "9000:9090"
    volumes:
      - data:/prometheus
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
    restart: unless-stopped
    extra_hosts:
      - localserver:192.168.1.2
        
volumes:
  data:
  
prometheus.yml:
  
global:
  scrape_interval: 15s
  evaluation_interval: 15s
scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: 
        - "localhost:9090"
        labels:
          app: "prometheus"
  - job_name: "mimir"
    static_configs:
      - targets:
        - "localserver:9100"
        labels:
          app: "mimir"
  - job_name: "gotoso"
    static_configs:
      - targets:
        - "localserver:9090"
        labels:
          app: "gotoso"    
  
  Isso já faz subir e você pode olhar no target health.
 
Daí é deixar o Prometheus coletar os dados e depois olhar os gráficos.
 
Ninguém me segurou, mas também não instalei o Grafana.
Por enquanto...
Um pequeno vídeo juntando todas a fotos que o Google Photos mostrou com a busca por "selfie".
 
E a bolha está de pé. Ou quase isso.
A primeira semana em operação foi erro 502 o tempo todo. Achei que o problema era como estava funcionando pelo systemd. Então criei um serviço novo só pra ela.
  
# /etc/systemd/user/podman-compose@.service
[Unit]
Description=GoToSocial as container service
StartLimitIntervalSec=0
[Service]
Type=simple
User=helio
Group=helio
#WorkingDirectory=/home/helio/gotosocial
ExecStart=/home/helio/gotosocial/entrypoint.sh start
ExecStop=/home/helio/gotosocial/entrypoint.sh stop
Restart=always
RestartSec=30
[Install]
WantedBy=default.target    
  
  Depois achei que era o enviroment.
  Comentei a parte de WorkingDirectory, como pode ser visto acima.
  Também troquei o podman-compose up por esse script entrypoint.sh.
  
#! /usr/bin/env bash
GOTOSOCIAL_DIR="/home/helio/gotosocial"
start_gotosocial() {
    echo "Starting gotosocial" 
    cd $GOTOSOCIAL_DIR
    /usr/bin/podman pull docker.io/superseriousbusiness/gotosocial:latest
    /usr/bin/podman pull docker.io/library/postgres:latest
    /usr/bin/podman-compose down
    sleep 5
    /usr/bin/podman-compose up
}
stop_gotosocial() {
    echo "Stopping GoToSocial"
    cd $GOTOSOCIAL_DIR
    /usr/bin/podman-compose down
}
case $1 in
        start) start_gotosocial ;;
        stop) stop_gotosocial ;;
        restart) $0 stop
             sleep 30
                     $0 start
                     ;;
        *) echo "Unknown option: $1" 
           exit 1
esac    
  
  Os podman pull estavam antes no serviço do systemd.
  Joguei tudo pra dentro do script.
  E o resultado foi: 502.
Então comecei a considerar que tinha feito algo errado no compose.yml.
  
services:
  gotosocial:
    image: docker.io/superseriousbusiness/gotosocial:latest
    container_name: gotosocial
    user: 1000:1000
    networks:
      - gotosocial
    environment:
      # Change this to your actual host value.
      GTS_HOST: bolha.linux-br.org
      GTS_DB_TYPE: postgres
      GTS_CONFIG_PATH: /gotosocial/config.yaml
      # Path in the GtS Docker container where the
      # Wazero compilation cache will be stored.
      GTS_WAZERO_COMPILATION_CACHE: /gotosocial/.cache
      ## For reverse proxy setups:
      GTS_TRUSTED_PROXIES: "127.0.0.1,::1,172.18.0.0/16"
      ## Set the timezone of your server:
      TZ: Europe/Stockholm
    ports:
      - "127.0.0.1:8080:8080"
    volumes:
      - data:/gotosocial/storage
      - cache:/gotosocial/.cache
      - ~/gotosocial/config.yaml:/gotosocial/config.yaml
    restart: unless-stopped
    healthcheck:
      test: wget --no-vebose --tries=1 --spider http://localhost:8080/readyz
      interval: 10s
      retries: 5
      start_period: 30s
    depends:
      - postgres
        
  postgres:
    image: docker.io/library/postgres:latest
    container_name: postgres
    networks:
      - gotosocial
    environment:
      POSTGRES_PASSWORD: *****
      POSTGRES_USER: gotosocial
      POSTGRES_DB: gotosocial
    restart: unless-stopped
    volumes:
      - ~/gotosocial/postgresql:/var/lib/postgresql
    ports:
      - "5432:5432"
    healthcheck:
      test: pg_isready
      interval: 10s 
      timeout: 5s
      retries: 5
      start_period: 120s
networks:
  gotosocial:
    ipam:
      driver: default
      config:
        - subnet: "172.18.0.0/16"
          gateway: "172.18.0.1"
volumes:
  data:
  cache:
  
Nada de muito fantástico. Um postgres rodando junto com um gotosocial. Algumas configurações de proxy, que é o nginx da máquina, e é isso. E continuava o 502.
  Mas se eu entrava na máquina, e rodava uma sessão de tmux e dentro dela chamava o podman-compose up,
  daí tudo funcionava.
  Dei então uma olhada no erro.
  
Oct 15 10:16:56 mimir entrypoint.sh[1895291]: podman-compose version: 1.0.6
Oct 15 10:16:56 mimir entrypoint.sh[1895291]: ['podman', '--version', '']
Oct 15 10:16:56 mimir entrypoint.sh[1895291]: using podman version: 4.9.3
Oct 15 10:16:56 mimir entrypoint.sh[1895291]: ** excluding:  set()
Oct 15 10:16:56 mimir entrypoint.sh[1895291]: ['podman', 'ps', '--filter', 'label=io.podman.compose.project=gotosocial', '-a', '--format', '{{ index .Labels "io.podman.compose.config-hash"}}']
Oct 15 10:16:56 mimir entrypoint.sh[1895303]: time="2025-10-15T10:16:56+02:00" level=warning msg="RunRoot is pointing to a path (/run/user/1000/containers) which is not writable. Most likely podman will fail."
Oct 15 10:16:56 mimir entrypoint.sh[1895303]: Error: default OCI runtime "crun" not found: invalid argument
Oct 15 10:16:57 mimir entrypoint.sh[1895291]: Traceback (most recent call last):
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:   File "/usr/bin/podman-compose", line 33, in 
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:     sys.exit(load_entry_point('podman-compose==1.0.6', 'console_scripts', 'podman-compose')())
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:   File "/usr/lib/python3/dist-packages/podman_compose.py", line 2941, in main
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:     podman_compose.run()
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:   File "/usr/lib/python3/dist-packages/podman_compose.py", line 1423, in run
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:     cmd(self, args)
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:   File "/usr/lib/python3/dist-packages/podman_compose.py", line 1754, in wrapped
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:     return func(*args, **kw)
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:            ^^^^^^^^^^^^^^^^^
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:   File "/usr/lib/python3/dist-packages/podman_compose.py", line 2038, in compose_up
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:     compose.podman.output(
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:   File "/usr/lib/python3/dist-packages/podman_compose.py", line 1098, in output
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:     return subprocess.check_output(cmd_ls)
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:   File "/usr/lib/python3.12/subprocess.py", line 466, in check_output
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:     return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:   File "/usr/lib/python3.12/subprocess.py", line 571, in run
Oct 15 10:16:57 mimir entrypoint.sh[1895291]:     raise CalledProcessError(retcode, process.args,
Oct 15 10:16:57 mimir entrypoint.sh[1895291]: subprocess.CalledProcessError: Command '['podman', 'ps', '--filter', 'label=io.podman.compose.project=gotosocial', '-a', '--format', '{{ index .Labels "io.podman.compose.config-hash"}}']' returned non-zero exit status 125.
Oct 15 10:16:57 mimir systemd[1]: gotosocial.service: Main process exited, code=exited, status=1/FAILURE
Oct 15 10:16:57 mimir systemd[1]: gotosocial.service: Failed with result 'exit-code'.
Oct 15 10:16:57 mimir systemd[1]: gotosocial.service: Consumed 1.481s CPU time.
Oct 15 10:17:27 mimir systemd[1]: gotosocial.service: Scheduled restart job, restart counter is at 1280.
Oct 15 10:17:27 mimir systemd[1]: Started gotosocial.service - GoToSocial as container service.
Oct 15 10:17:27 mimir entrypoint.sh[1895707]: Starting gotosocial
Oct 15 10:17:30 mimir entrypoint.sh[1895781]: podman-compose version: 1.0.6
Oct 15 10:17:30 mimir entrypoint.sh[1895781]: ['podman', '--version', '']
Oct 15 10:17:30 mimir entrypoint.sh[1895781]: using podman version: 4.9.3
Oct 15 10:17:30 mimir entrypoint.sh[1895781]: ** excluding:  set()
Oct 15 10:17:30 mimir entrypoint.sh[1895781]: podman stop -t 10 postgres
Oct 15 10:17:31 mimir entrypoint.sh[1895781]: exit code: 0
Oct 15 10:17:31 mimir entrypoint.sh[1895781]: podman stop -t 10 gotosocial
Oct 15 10:17:31 mimir entrypoint.sh[1895781]: exit code: 0
Oct 15 10:17:31 mimir entrypoint.sh[1895781]: podman rm postgres
Oct 15 10:17:31 mimir entrypoint.sh[1895781]: exit code: 0
Oct 15 10:17:31 mimir entrypoint.sh[1895781]: podman rm gotosocial
Oct 15 10:17:31 mimir entrypoint.sh[1895781]: exit code: 0
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: podman-compose version: 1.0.6
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: ['podman', '--version', '']
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: using podman version: 4.9.3
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: ** excluding:  set()
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: ['podman', 'ps', '--filter', 'label=io.podman.compose.project=gotosocial', '-a', '--format', '{{ index .Labels "io.podman.compose.config-hash"}}']
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: podman volume inspect gotosocial_data || podman volume create gotosocial_data
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: ['podman', 'volume', 'inspect', 'gotosocial_data']
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: podman volume inspect gotosocial_cache || podman volume create gotosocial_cache
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: ['podman', 'volume', 'inspect', 'gotosocial_cache']
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: ['podman', 'network', 'exists', 'gotosocial_gotosocial']
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: podman create --name=gotosocial --label io.podman.compose.config-hash=4f4b10e0c67c04b7b4f2392784b378735d4378d9d411f1405cf3819c6207bd1a --label io.podman.compose.project=gotosocial --label io.podman.compose.version=1.0.6 --label PODMAN_SYSTEMD_UNIT=This email address is being protected from spambots. You need JavaScript enabled to view it.  --label com.docker.compose.project=gotosocial --label com.docker.compose.project.working_dir=/home/helio/gotosocial --label com.docker.compose.project.config_files=compose.yaml --label com.docker.compose.container-number=1 --label com.docker.compose.service=gotosocial -e GTS_HOST=bolha.linux-br.org -e GTS_DB_TYPE=postgres -e GTS_CONFIG_PATH=/gotosocial/config.yaml -e GTS_WAZERO_COMPILATION_CACHE=/gotosocial/.cache -e GTS_TRUSTED_PROXIES=127.0.0.1,::1,172.18.0.0/16 -e TZ=Europe/Stockholm -v gotosocial_data:/gotosocial/storage -v gotosocial_cache:/gotosocial/.cache -v /home/helio/gotosocial/config.yaml:/gotosocial/config.yaml --net gotosocial_gotosocial --network-alias gotosocial -p 127.0.0.1:8080:8080 -u 1000:1000 --restart unless-stopped --healthcheck-command /bin/sh -c 'wget --no-vebose --tries=1 --spider http://localhost:8080/readyz' --healthcheck-interval 10s --healthcheck-start-period 30s --healthcheck-retries 5 docker.io/superseriousbusiness/gotosocial:latest
Oct 15 10:17:36 mimir entrypoint.sh[1895920]: exit code: 0
    
   
  A parte final, com podman create, é o systemd reiniciando o serviço.
  O problema está on início, onde há um crash de python:
   subprocess.CalledProcessError: Command '['podman', 'ps', '--filter', 'label=io.podman.compose.project=gotosocial', '-a', '--format', '{{ index .Labels "io.podman.compose.config-hash"}}']' returned non-zero exit status 125.
Eu entrava na máquina e rodava o comando pra ver o resultado:
  
❯ podman ps --filter 'label=io.podman.compose.project=gotosocial' -a --format '{{ index .Labels "io.podman.compose.config-hash"}}'
4f4b10e0c67c04b7b4f2392784b378735d4378d9d411f1405cf3819c6207bd1a
4f4b10e0c67c04b7b4f2392784b378735d4378d9d411f1405cf3819c6207bd1a    
  
E mostrava os containers rodando (porque tinha sido reiniciados pelo systemd). Eu ficava com aquela cara de "ué!?".
  No início do erro, tem essa outra mensagem aqui:
  
    Error: default OCI runtime "crun" not found: invalid argument
  
  .
  Então fui olhar se era algum problema nesse crun.
  E está instalado (acho que veio como dependência do podman.
  
❯ which crun
/usr/bin/crun
❯ dpkg -S /usr/bin/crun
crun: /usr/bin/crun
  
Busquei sobre erros do GoToSocial mesmo. E nada.
  Olhando pra todo lado tentando descobrir o que poderia ser, reparei em outro erro:
  
    msg="RunRoot is pointing to a path (/run/user/1000/containers) which is not writable. Most likely podman will fail."
  
  .
  Isso soou promissor.
  Então de repente o pointing path não estava disponível pra escrita.
  Poderia ser... systemd?
  Com isso eu comecei a buscar algo relacionado com timeout ou user logout.
  Acabei encontrando o artigo abaixo:
  Nesse artigo alguém comenta que pode ser uma opção de container linger.
  Segui a referência que tinha sobre isso.
  loginctl?
  Faz até sentido isso.
  Mas o podman não deveria descrever isso na documentação?
  Então fui buscar e achei isso aqui:
  Pra deixar bem ilustrado onde aparece a referência de linger na documentação:
 
Algo que é vital pra funcionar como serviço aparece como... exemplo??? Os caras tão de brincation uite me.
 
  Mas no fim era isso mesmo.
  Bastou um sudo logictl enable-user helio pra ter o container rodando depois que eu saio da sessão.
  Se eu tivesse decido rodar com docker compose, eu provavelmente não teria o mesmo problema uma vez que roda com o privilégio de root.
  Então fica mais essa lição aqui.
  E mesmo tendo lendo a documentação, sempre aparecem alguns pontos que a porra da documentação só dá um peteleco em cima e dentro dos exemplos ainda por cima.
Mas está funcionando. Minha bolha, bolha minha.
 
Faz algum tempo que venho pensando e criar meu próprio servidor no fediverso. O Augusto Campos postou sobre como fez a mesma coisa com uns NUC, aqueles computadores compactos. Eu fiquei empolgado com a ideia e resolvi ir pra frente.
Minha primeira ideia foi de rodar tudo num raspberry pi 5. Mas daí veio primeiro dilema: preço.
 
Por 1.500 coroas suecas é um pouco demais pra algo sem disco. É divertido, mas está longe do que eu queria.
Então resolvi olhar se tinha algo refurbished, pra pagar barato e ajudar a natureza (será que ajuda mesmo?). E achei algo bem interessantes na Amazon mesmo.
 
1.600 coroas suecas por 16 GB de RAM, 512 GB de SSD? E ainda uma CPU Xeon? E mesmo placa de vídeo???
Bora lá!
Comprei a máquina. E ela chegou ontem.
 
Primeira coisa que fiz, logo depois que tirei da caixa, foi abrir pra ver como estava dentro. Tem bastante espaço sobrando dentro pra aumentar a RAM.
 
E até mesmo espaço pra mais discos. Já estou pensando em comprar um HDD de 8 TB pra colocar também como storage pra backups.
 
E ainda veio uma plaquinha de vídeo marota. Coisa linda.
 
E como extra ainda vieram juntos teclado e mouse. Tem cara de gamer mas olhando mais de perto o teclado...
 
   
Esperava um switch mecânico blue, mas no lugar esse switch esquisito aí. Tem cara de coisa barata. Mas tem luzinha colorida. Já vale como brinde gratuito. E falando de brindes...
 
Veio também esse dongle bluetooth. Eu tenho um parecido no desktop que uso aqui, que roda archlinux btw.
Com o brinquedo em mão, já fui pra instalação. Fui de Ubuntu mesmo. E com zfs como filesystem.
 
   
  A máquina está instalada e decidi usar o hostname como mimir.
  Não muito criativo, mas um símbolo nórdico de conhecimento.
  Tá valendo.
Eu ainda estou vendo como vou fazer tudo. A máquina está ligada e conectada na rede. Está aqui no meu quarto, ao lado da impressora. Talvez eu depois mude pra outro lugar mas como está silenciosa, então não é problema por enquanto.
Dos desafios pra subir a máquina estão as limitações de um ambiente home based: não tenho backup de nada, não tem no-break e só conectividade por IPv6. Além de que não posso ter serviço de email configurado uma vez que a porta de SMTP (25) fica bloqueada.
Também não decidi ainda o que vou rodar. Estou pensando em Pixelfed pra uma forma de postar fotos. Mas nada decidido ainda. Continuo testando.
Mas logo devo anunciar minha própria rede social. Aguardem.
 
Essa semana recebi uma missão: permitir acesso aos logs de um container numa VM em que a pessoa não pode conectar.
Pense em algumas opções e a que me pareceu mais apropriada foi criar um pequeno serviço com python, fastapi e uvicorn. E deixar disponível como acesso http.
Então fiz um programa bem simples:
  
#! /usr/bin/env python3
import uvicorn
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse, StreamingResponse
import subprocess
# https://stackoverflow.com/questions/4417546/constantly-print-subprocess-output-while-process-is-running
def shellExec(command: list[str]):
    popen = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True)
    for stdout_line in iter(popen.stdout.readline, ""):
        yield stdout_line
    popen.stdout.close()
    return_code = popen.wait()
    if return_code:
        raise subprocess.CalledProcessError(return_code, command)
def getContainerLogs():
    for line in shellExec(["docker", "logs", "ubuntu", "-f"]):
        yield  line
app = FastAPI()
@app.get("/logs", response_class=PlainTextResponse)
async def getLogs():
    return StreamingResponse(getContainerLogs(), media_type="text/plain")
if __name__ == '__main__':
    uvicorn.run(app, host="0.0.0.0", port=8080)    
  
  O programa então roda o comando docker logs ubuntu -f pra ficar lendo os logs vindo do container "ubuntu".
  Nada muito fantástico.
E como deixar rodando?
  Eu podia criar um container que pudesse acessar /var/run/docker.socket pra ler info dos containers rodando.
  E os logs.
  Mas fui pela simplicidade e só criei um serviço do systemd mesmo.
  
[Unit]
Description=Stream logs from ubuntu container
Wants=network-online.target
After=network-online.target docker.service
[Service]
User=helio
Group=hackerz
Restart=always
WorkingDirectory=/home/helio/bin
ExecStart=/home/helio/bin/stream-logs-container.py
# If running the Agent in scraping service mode, you will want to override this value with
# something larger to allow the Agent to gracefully leave the cluster. 4800s is recommend.
TimeoutStopSec=5s
[Install]
WantedBy=multi-user.target    
  
Daí bastou ativar e partir pro abraço.
  
❯ sudo systemctl enable --now stream-logs-container.service
Password:
❯ curl localhost:8080/logs
mariadb 12:38:12.20 INFO  ==> 
mariadb 12:38:12.21 INFO  ==> Welcome to the Bitnami mariadb container
mariadb 12:38:12.21 INFO  ==> Subscribe to project updates by watching https://github.com/bitnami/containers
mariadb 12:38:12.21 INFO  ==> Did you know there are enterprise versions of the Bitnami catalog? For enhanced secure software supply chain features, unlimited pulls from Docker, LTS support, or application customization, see Bitnami Premium or Tanzu Application Catalog. See https://www.arrow.com/globalecs/na/vendors/bitnami/ for more information.
mariadb 12:38:12.22 INFO  ==> 
mariadb 12:38:12.22 INFO  ==> ** Starting MariaDB setup **
mariadb 12:38:12.25 INFO  ==> Validating settings in MYSQL_*/MARIADB_* env vars
mariadb 12:38:12.26 INFO  ==> Initializing mariadb database
mariadb 12:38:12.28 INFO  ==> Updating 'my.cnf' with custom configuration
mariadb 12:38:12.29 INFO  ==> Setting slow_query_log option
mariadb 12:38:12.35 INFO  ==> Setting long_query_time option
mariadb 12:38:12.37 INFO  ==> Installing database
mariadb 12:38:13.91 INFO  ==> Starting mariadb in background
2025-10-02 12:38:13 0 [Note] Starting MariaDB 10.11.11-MariaDB source revision e69f8cae1a15e15b9e4f5e0f8497e1f17bdc81a4 server_uid RV0GswTTbCaNJgiFfL+XFbloFPM= as process 98
2025-10-02 12:38:13 0 [Note] InnoDB: Compressed tables use zlib 1.2.13
2025-10-02 12:38:13 0 [Note] InnoDB: Number of transaction pools: 1
2025-10-02 12:38:13 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
2025-10-02 12:38:14 0 [Note] InnoDB: Using Linux native AIO
2025-10-02 12:38:14 0 [Note] InnoDB: Initializing buffer pool, total size = 128.000MiB, chunk size = 2.000MiB
2025-10-02 12:38:14 0 [Note] InnoDB: Completed initialization of buffer pool
2025-10-02 12:38:14 0 [Note] InnoDB: Buffered log writes (block size=512 bytes)
2025-10-02 12:38:14 0 [Note] InnoDB: End of log at LSN=45502
2025-10-02 12:38:14 0 [Note] InnoDB: 128 rollback segments are active.
2025-10-02 12:38:14 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
2025-10-02 12:38:14 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
^C
  
Como ele fica lendo sem parar os logs, é preciso um "ctrl+c" pra sair.
 
Conformes as pedaladas foram ficando mais longas, eu comecei a ter um problema recorrente: câimbras.
Sempre depois de 60 Km e quando pedalando próximo de 20 Km/h (ou acima). E uma vez que a câimbra chega, não tem como fazer parar. Só resta pedalar bem mais devagar. E isso pode ser um problema numa pedalada planejada de 100 Km.
O que fiz? Não, não perguntei ao ChatGPT.
Mas busquei no Youtube.
E achei um vídeo falando do assunto.
Gosto bastante dos vídeos desse canal GCN. Dão várias dicas boas. E essa não foi exceção.
Eu passei a usar o que indicaram no vídeo: eletrólitos. Eu estou usando esse eletrólito aqui no momento:
Basicamente ele adiciona açúcar, pra dar energia, mantém hidratado porque é uma mistura com água, e tem um pouco de sal. E você pode até mesmo fazer em casa uma vez que a receita é a mesma que soro caseira.
E vou dizer que isso mudou muito a forma como pedalo hoje em dia. Antes eu tinha o receio de ter a câimbra no meio do caminho e ia mais leve. Agora eu estou me arrebentando de pedalar e sinto os músculos fadigados, mas nada de câimbra. E as pedaladas passaram de acima 60 Km pra mais de 100 Km.
Não sei se o eletrólito funciona da mesma forma pra todo mundo. Os motivos de câimbras podem ser diferentes pra cada pessoa. Mas acho que vale a tentativa de usar pra quem está sofrendo com isso.
 
 
Já faz algum tempo que venho trocando o hábito de comprar na Amazon pelo de comprar na Aliexpress. A vantagem é que geralmente é mais barato na Aliexpress. A desvantagem é que nem tudo dá pra trocar ou devolver. E demora mais pra chegar, se bem que os chineses estão fazendo um excelente trabalho nesse ponto. E tem o fator qualidade.
Então essas compras invariavelmente te levam a adquirir coisas que não duram muito ou estragam rapidamente.
Mas isso está mudando. E muito. Então vou listar aqui coisas que comprei ultimamente e estão sendo muito úteis e a qualidade é excelente. Vou botar os links mas... na Aliexpress os vendedores podem desaparecer do dia pra noite. Faz parte do "ethos" da Aliexpress. Vamos então às dicas.
 
Não é lá um produto que exigem muita coisa, mas é algo que ajuda bastante nos dias de chuva pra não deixar sua cabeça molhada. E custa pouco.
 
Essa bomba portátil ajudou a diminuir o peso e volume da bomba elétrica USB que eu carregava antes. Eu sempre levo uma outra bomba manual pro caso de ficar sem gás já que usou o cilindro uma vez, ele não segura o gás depois.
 
Eu tenho uma mochila maior que levo nas viagens mais longas. Mas uso essa pras pedaladas mais próximas. Como eu tenho bagageiro, prendo nele ao invés de prender no banco.
 
Conforme vamos pedalando, essa fita do guidão vai gastando. Troquei por essa da Aliexpress e não tenho reclamações. Fuciona bem e a opção de cores é maior.
 
Quando pedalamos no verão, depois de 2 ou 3 horas, a água das garrafas viram chá. Então eu comprei essa térmica pra experimentar. E funciona! Mantém o líquido gelado por mais de 12 horas. O problema foi tamanho: diâmetro maior que o que cabia no porta-garrafa da bike. Atualmente eu carrego ela como segunda garrafa na mochina traseira.
 
Eu não sei muito bem como descrever essas peças de roupa de ciclismo em português que em tradução literal ficariam como "aquecedores de pernas e braços", mas são extensões de roupas pra te manter quente. Talvez não muito necessário em climas quentes como do Brasil, mas essenciais aqui na Europa nórdica. E ocupam pouco espaço na mala.
 
Eu uso essa camiseta como primeira camada. Não tem outra funcionalidade além da de absorver suor e evitar aquela gotícula que escorre das costas e entra no seu rego e só vai parar quando chega no seu c*. Pra quem não usa bib pra pedalar, aquela roupa de ciclismo, ou usa essa roupa com roupas de baixo (calcinha ou cueca) - o que não deveriam fazer, não é algo tão necessário assim.
 
Essa é a fita que uso pra fazer o acabamento da outra fita, que descrevi acima, no guidão. Usei recentemente pra cobrir o cabo do power bank, que é laranja, e deixar quase todo preto.
 
Como a outra garrafa térmica que comprei primeiro tem o diâmetro muito grande, acabei comprando essa. Ela é hoje em dia minha garrafa principal e segura bem a temperatura. Talvez por até 10h ou mais. E com o diâmetro menor, encaixa sem problemas no porta-garrafas da bike.
 
A maioria dos porta-garrafas você tira por cima a garrafa. Como eu uso um porta trecos na parte de baixo do frame da bike, pra carregar o power bank, precisei comprar um que eu pudesse tirar e colocar a garrafa de lado. Esse permite garrafas com diâmetro maior mas eu achei que a garrafa térmica de 1l não fica firme nela e resolvi não arriscar.
 
Item obrigatório aqui, que chove bastante. A vantagem desses paralamas é que são presos por borrachas. Então é fácil colocar e tirar.
 
Deve ter nome melhor que "porta-trecos" mas essa mochila que é um porta-trecos tem o diferencial de poder parafusar no frame. E como minha bike, uma Trek, tem essa entrada pros parafusos, esse porta-treco funcionou maravilhosamente.
 
Eu fiz um review no Aliexpress sentando a lenha nesse power bank. Fui muito rápido no meu teste pra fazer tal review. O problema é que o cabo não entra muito bem e precisa encaixar certo. Feito isso corretamente, segura bem a carga. Alimenta a minha GoPro por 12h. Até mais. Tem a funcionalidade de lâmpada, mas eu não uso. E encaixa no conector de Garmin. A parte de cima, onde coloco o computador, não é muito firme e faz barulho. Mas não cai. Nem solta.
 
Não é bem relacionado com a bike, mas como uso um setup Garmin, digamos que até faz parte. Eu não gosto de usar a pulseira de borracha e prefiro essas de velcro.
 
Acho que nem preciso descrever muito aqui. Talvez só uma nota: meu amigo comprou uma semelhante pela Temu e a qualidade parece inferior.
 
Também acho que não preciso descrever muito aqui. Funciona bem pra absorver o suor.
 
Aqui capacete não é ítem obrigatório. Mas campainha é. E essa fica legal em drop bar de road e gravel bikes.
 
Se realmente protege contra raios UV, eu não sei. Mas tem funcionado. Tem uns vãos nas bordas que incomodam no início. Mas depois de algumas pedaladas você acostuma e não nota. Eu não costumo botar as alças laterais atrás da orelha porque vão machucando ao longo do caminho. Eu coloco ao lado e preciso que fique firme pra não cair (o capacete ajuda a segurar). E esses óculos funcionaram bem pra isso.
 
Esse espelho ajuda muito quando estamos na estrada. Posso ver se quem está atrás não está distante e ver se carros estão chegando. E quando fica fechado, não fica aparente que está ali no guidão.
 
Esse aqui não tem um uso muito prático. É só estético mesmo. Eu tenho a tampa das válvulas de alumínio azul também. Então combina e fica charmoso.
 
Quando ligo o power bank ou no telefone ou na GoPro, esse é o cabo que uso. Ele estica bastante e não fica solto.
 
Comprei só pra combinar com a cor da bike. Funcionam bem.
 
Eu passei a usar esse mount de Garmin pra segurar o telefone. Fica firme. E posso encaixar o computador também. E os power banks que tenho. Podia ter em azul pra combinar com a bike.
 
Mais fácil pra prender o tênis.
 
Eu preciso trocar os pedais a cada 6 meses. Talvez até em menos tempo. Esses são baratos e leves.
 
São engraçadas. Mas não absorvem bem o suor. Mas são engraçadas.
 
Essas luvas com alcochoamento mais grosso (6mm) ajudam bastante nas pedaladas mais longas.
 
Como eu tenho mount de Garmin na bike, uso esse case pra segurar o telefone. Fica firme e absorve bem a trepidação.
Um dos produtos desapareceu. Então vou deixar ele aqui pra caso apareça de novo. É um power bank com lâmpada. Tenho usado mais como lâmpada frontal.
 
A descrição era: ThinkRider Bicycle Light For Garmin Gopro Bike Headlight Type-C Usb Charging Bike Flashlight Bicycle Lamp Black, 100-300 Lumen.
Agora não tem mais desculpa pra nãp pedalar. Bugigangas legais é que não vão faltar.
 
Agora dando uma olhada na Aliexpress, eu achei o powerbank com lâmpada que mencionei. Mudou de loja/fabricante mas parece o mesmo produto.
 
Por motivos que não cabem aqui, temos alguns servidores instalados em uma sala na empresa. E essa sala tem um ar-condicionado pra manter a temperatura sob controle.
Um belo dia estou olhando os dados no Grafana e noto que essas máquinas não reportaram dados (não temos alertas enviados pelo Grafana, mas o porquê disso fica pra um outro dia). Entro na sala e a temperatura estava simplemente... 32°C. Era verão na Suécia, que é curto mas tem seus dias bem quentes. E o hardware das máquinas desligaram pra proteção.
Entre entrar em contato com técnico do ar-condicionado e deixar a sala aberta pra ventilar, ficamos com aquele gosto amargo de não ter nenhum dado sobre a temperatura.
A solução? raspberrypi!
Ele tem um sensor que é vendido na Internet.
 
O sensor já chegou mas não o raspberrypi. O motivo deve ser porque compramos um modelo que funciona como KVM e tem algumas coisas a mais.
Então aqui a descrição de como botar o serviço pra funcionar em Linux.
Existe um software descrito na página do produto que aponta pro seguinte repositório no GitHub:
Mas o repositório parece abandonado. Já faz 7 anos que ninguém manda nenhum commit. E o código não funciona com a versão mais moderna do sensor.
O que fazer? Patch!
Então corrigi o programa e criei um fork do repo original.
Então temos o software pronto pra funcionar. Ou quase.
    Antes é preciso corrigir as permissões de leitura e escrita do dispositivo.
    E pra isso eu criei uma pequena regra no udev em 
    /etc/udev/rules.d/90-temperature-sensor.rules
  
    
KERNEL=="hidraw[0-9]*", SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="3553", ATTRS{idProduct}=="a001", MODE="0666", SYMLINK+="temper"      
    
  
  Pegando a saída do kernel:
    
# dmesg | grep -i temper
[    5.152423] usb 1-10.4: Product: TEMPer2
[    6.769788] input: PCsensor TEMPer2 as /devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10.4/1-10.4:1.0/0003:3553:A001.0005/input/input14
[    6.826541] hid-generic 0003:3553:A001.0005: input,hidraw4: USB HID v1.11 Keyboard [PCsensor TEMPer2] on usb-0000:00:14.0-10.4/input0
[    6.826720] input: PCsensor TEMPer2 as /devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10.4/1-10.4:1.1/0003:3553:A001.0006/input/input15
[    6.827067] hid-generic 0003:3553:A001.0006: input,hidraw5: USB HID v1.10 Device [PCsensor TEMPer2] on usb-0000:00:14.0-10.4/input1
[  939.507362] usb 1-10.4: Product: TEMPer2
[  939.521617] input: PCsensor TEMPer2 as /devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10.4/1-10.4:1.0/0003:3553:A001.000B/input/input26
[  939.580825] hid-generic 0003:3553:A001.000B: input,hidraw4: USB HID v1.11 Keyboard [PCsensor TEMPer2] on usb-0000:00:14.0-10.4/input0
[  939.581808] input: PCsensor TEMPer2 as /devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10.4/1-10.4:1.1/0003:3553:A001.000C/input/input27
[  939.582035] hid-generic 0003:3553:A001.000C: input,hidraw5: USB HID v1.10 Device [PCsensor TEMPer2] on usb-0000:00:14.0-10.4/input1
    
  
  
    É possível ver que o dispositivo aparece como dois devices: 
    /dev/hidraw4
    e
    /dev/hidraw5.
  
Eu tentei usar a permissão 0644 primeiro, mas essa não funcionou pra ler os dados. Então tive de mudar pra 0666 mesmo sendo algo que só lê informação.
    Feita essa etapa, ainda não estamos prontos pra rodar o programa temper.py.
    Ainda é preciso instalar a dependência: serial.
  
Se seu sistema é baseado em debian/ubuntu:
    
> sudo apt install -y python3-serial
    
  
  
    Se não for, talvez seja mais fácil fazer com virtualenv.
    E pra isso eu atualmente uso o uv
  
   
> uv venv venv
> source venv/bin/activate     
(venv)> uv pip install serial
   
  
 Tendo tudo pronto, chegamos ao momento da verdade:
   
(venv)> ./temper.py 
Bus 001 Dev 011 3553:a001 TEMPer2_V4.1 25.6C 78.0F - 22.8C 73.1F -
   
 
 Pegando a saída como JSON permite ver melhor o que é cada um desses resultados.
   
(venv)>  ./temper.py --json
[
    {
        "vendorid": 13651,
        "productid": 40961,
        "manufacturer": "PCsensor",
        "product": "TEMPer2",
        "busnum": 1,
        "devnum": 11,
        "devices": [
            "hidraw4",
            "hidraw5"
        ],
        "firmware": "TEMPer2_V4.1",
        "hex_firmware": "54454d506572325f56342e3100000000",
        "hex_data": "808009f64e200000800108e94e200000",
        "internal temperature": 25.5,
        "external temperature": 22.81
    }
]     
   
 
 
   Então a primeira temperatura lida, de /dev/hidraw4, é 25.5°C interna do dispositivo.
   A segunda, /dev/hidraw5, é de 22.81°C e externa, do cabo.   
 
 
Temos as leituras e os dados. Como mandar isso pro Grafana?
Eu primeiramente tentei fazer em shell script e mandar o dados pro mimir, que é onde eu agrego as métricas.
Fracassei miseravelmente.
Não existe uma forma muito fácil de enviar um dados pro lá. O formato que o alloy usa é protobuf, que é um dado comprimido em snappy, etc.
Qual outra alternativa?
Expor o dado como open metric pro alloy pegar e enviar.
   Pode parecer simples mas... precisamos de um servidor web pra isso.
   Algo que temos fácil em python.
   Então usando uvicorn e fastapi podemos ter tudo funcionando.
   E é possível importar o temper.py como módulo.
 
E é preciso incrementar nosso virtualenv (ou pacotes) com esses pacotes:
  
> sudo apt install -y python3-uvicorn python3-fastapi
  
ou
  
(venv)> uv pip install uvicorn
(venv)> uv pip install fastapi
  
 
   Sem mais delongas, eis aqui o código do monitor.py:
 
   
#! /usr/bin/env python3
import subprocess
import argparse
import logging
import threading
import time
try:
    import temper
except ImportError as e:
    print(f"Error importing temper module: {e}")
    print("Make sure python3-serial is installed: sudo apt-get install python3-serial")
    exit(1)
import uvicorn
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
CELSIUS = "\u2103"
DEFAULT_PORT = 8000
TEMPERATURE_MAX = 25.0
logger = logging.getLogger(__file__)
consoleOutputHandler = logging.StreamHandler()
formatter = logging.Formatter(
    fmt="[%(asctime)s] (%(levelname)s) %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)
consoleOutputHandler.setFormatter(formatter)
logger.addHandler(consoleOutputHandler)
logger.setLevel(logging.INFO)
def shellExec(command: str) -> str:
    'run a command and return its output'
    try:
        return subprocess.getoutput(command)
    except Exception as e:
        logger.error(f"Error executing shell command '{command}': {e}")
        return f"Error: {e}"
app = FastAPI()
# Global temperature monitor instance (will be set in main)
temperature_monitor = None
@app.get("/metrics", response_class=PlainTextResponse)
async def metrics():
    if temperature_monitor is None or temperature_monitor.temperature_current is None:
        return ""
        
    temperature_current = temperature_monitor.temperature_current
    logger.info(f"/metrics: {temperature_current}{CELSIUS}")
    data_lines = list()
    data_lines.append("#HELP server_room_temperature_celsius the room with servers current temperature")
    data_lines.append("#TYPE server_room_temperature_celsius gauge")
    data_lines.append(f"server_room_temperature_celsius {temperature_current}")
    data_lines.append("")
    return "\n".join(data_lines)
class TemperatureMonitor:
    'A class that handle the temperature monitoring'
    port: int = DEFAULT_PORT
    temperature_max: float = TEMPERATURE_MAX
    temperature_current: float|None = None
    alert_lock: bool = False
    def __init__(self, port=None, temperature_max=None) -> None:
        if port:
            self.port = port
        if temperature_max:
            self.temperature_max = temperature_max
    def monitor(self) -> None:
        th = threading.Thread(target=self.webserver)
        th.daemon = True  # Make it a daemon thread
        th.start()
        try:
            while True:
                self.update()
                time.sleep(15)
        except KeyboardInterrupt:
            logger.info("Monitoring stopped by user")
        except Exception as e:
            logger.error(f"Error in monitoring loop: {e}")
            raise
     
    def webserver(self) -> None:
        uvicorn.run(app, host="127.0.0.1", port=self.port)
    def update(self) -> None:
        'Read the output from the command'
        try:
            tp = temper.Temper().read()
            if not tp or len(tp) == 0:
                logger.warning("No temperature devices found")
                self.temperature_current = None
                return
            
            self.temperature_current = tp[0].get('external temperature')
            if self.temperature_current is not None:
                logger.info(f"🌡️ current temperature: {self.temperature_current}{CELSIUS}")
            else:
                logger.warning("External temperature reading is None")
        except Exception as e:
            logger.error(f"Error reading temperature sensor: {e}")
            self.temperature_current = None
if __name__ == '__main__':
    parse = argparse.ArgumentParser(description="script to monitor temperature")
    parse.add_argument("--loglevel", default="info", help="the logging level (default=info)")
    parse.add_argument("--tempmax", type=float, default=TEMPERATURE_MAX, help="maximum temperature before raising alert")
    parse.add_argument("--port", type=int, default=DEFAULT_PORT, help="port to listen the service")
    args = parse.parse_args()
    # Validate log level
    valid_log_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
    if args.loglevel.upper() not in valid_log_levels:
        print(f"Invalid log level: {args.loglevel}. Valid options: {', '.join(valid_log_levels)}")
        exit(1)
        
    if args.loglevel.upper() != "INFO":
        logger.setLevel(args.loglevel.upper())
    # Validate temperature threshold
    if args.tempmax <= 0:
        print("Temperature threshold must be greater than 0")
        exit(1)
        
    # Validate port number
    if not (1 <= args.port <= 65535):
        print("Port must be between 1 and 65535")
        exit(1)
    # Create temperature monitor instance
    temperature_monitor = TemperatureMonitor(args.port, args.tempmax)
    temperature_monitor.monitor()     
   
 
Eu tenho integrado uma parte de alerta que usa outro sistema, mas removi pra deixar o código fazendo somente o que é preciso.
No alloy, adicionei as seguintes linhas:
  
discovery.relabel "temperature_sensor" {
        targets = array.concat(
                [{
                __address__ = "localhost:8000",
                }],
        )
        rule {
                source_labels = ["__address__"]
                target_label  = "instance"
                replacement   = "temper"
        }
}
prometheus.scrape "temperature_sensor" {
        targets    = discovery.relabel.temperature_sensor.output
        forward_to = [prometheus.remote_write.prod.receiver]
        job_name   = "agent"
}    
  
  Tudo pronto.
  Ou quase.
  Falta rodar o monitor.py que mostrei acima como serviço.
  E pra isso usamos o systemd.
  Basta criar o arquivo /etc/systemd/system/temperature-monitor.service
  e iniciar.
  
[Unit]
Description=Temperature monitoring service
After=network.target
[Service]
User=helio
Group=helio
WorkingDirectory=/home/helio/temperature-sensor
ExecStart=/home/helio/temperature-sensor/monitor.sh
Restart=always
[Install]
WantedBy=multi-user.target    
  
  O script monitor.sh é pra somente ler o virtualenv corretamente:
  
#! /usr/bin/env bash
die() {
	echo "ERROR: $@" >&2
	echo "[$(date)] exiting with error"
	exit 1
}
program="$0"
root_dir=$(readlink -f $program)
root_dir=$(dirname $root_dir)
cd $root_dir
source venv/bin/activate || \
    die "failed to read virtualenv"
exec ./monitor.py    
  
E iniciando o serviço:
  
> sudo systemctl daemon-reload
> sudo systemctl enable --now temperature-monitor    
  
  O que resta é criar um gráfico pra métrica server_room_temperature_celsius
  e partir pro abraço.
 
Update: [Fri Sep 12 05:30:00 PM CEST 2025] acabo de perceber que o repositório que fiz fork é na verdade um fork de outro, que parece ser bem mais completo.
 
Seguindo o artigo expondo as métricas do nginx pro prometheus, aqui está o resultado olhado no grafana.
  Como os valores 
  server_accepts_total,
  server_handled_total e
  server_requests_total
  são do tipo counter,
  eu usei um irate(__variável__[5m])
  pra mostrar no gráfico da forma acima.
E como coletei esses dados? Eu já tinha comentado no artigo configurando o grafana alloy pra monitorar VMs que uso o alloy. Então foi adicionando uma entrada extra nele.
  
[...]
discovery.relabel "metrics_agent" {
        targets = array.concat(
                [{
                        __address__ = "localhost:9090",
                }],
        )
        rule {
                source_labels = ["__address__"]
                target_label  = "instance"
        }
}
[...]
prometheus.scrape "nginx_metrics" {
        targets    = discovery.relabel.metrics_agent.output
        forward_to = [prometheus.remote_write.prod.receiver]
}
  
Agora fica mais fácil entender o que acontece com as páginas servidas e se tem realmente alguma lentidão.
 
Esses dias eu peguei um problema no servidor web, nginx. Não nele especificamente. Mas um usuário estava reclamando que estava super lento pra carregar as páginas.
A questão então é como ver como e quanto está o nginx. Infelizmente a versão open source não fornece muita coisa. Só uma versão texto de estatísticas.
Não é grande coisa mas pelo menos já é ALGUMA COISA.
Agora como monitorar isso no Grafana?
A resposta são open metrics. E isso não tem.
Não tinha.
Fiz um programa em Go que converte essas estatísticas em open metrics e expõe na porta 9090 no endpoint /metrics.
Pra ter isso funcionando, é preciso primeiro subir a configuração de estatísticas no nginx.
server {
    listen 127.0.0.1:8080;
    location /api {
        stub_status;
        allow 127.0.0.1;
        deny all;
    }
}  
  Eu salvei no arquivo statistics.conf e coloquei em /etc/nginx/conf.d.
  E bastou um reload pra ter funcionado.
  
❯ curl localhost:8080/api
Active connections: 2 
server accepts handled requests
 21 21 322 
Reading: 0 Writing: 1 Waiting: 1   
  
Agora é rodar o programa e apontar pra esse endpoint.
  
❯ ./nginx-openmetrics/nginx-openmetrics --service=http://localhost:8080/api
[2025-08-22T14:11:45] (INFO): 🚚 fetching data from:http://localhost:8080/api
[2025-08-22T14:11:45] (INFO): 🎬 starting service at port:9090    
  
E a porta fica exposta pras métricas serem coletadas pelo prometheus ou grafana alloy. Ou qualquer outro programa que faça scrape de dados no padrão open metrics.
  
❯ curl localhost:9090/metrics
# HELP active_connections The number of active connections
# TYPE active_connections gauge
active_connections 1
# HELP reading_connections The number of active reading connections
# TYPE reading_connections gauge
reading_connections 0
# HELP server_accepts_total The total number of server accepted connections
# TYPE server_accepts_total counter
server_accepts_total 22
# HELP server_handled_total The total number of server handled connections
# TYPE server_handled_total counter
server_handled_total 22
# HELP server_requests_total The total number of server requests
# TYPE server_requests_total counter
server_requests_total 333
# HELP waiting_connections The number of waiting connections
# TYPE waiting_connections gauge
waiting_connections 0
# HELP writing_connections The number of active writing connections
# TYPE writing_connections gauge
writing_connections 1  
  
E fica exposto em todas as interfaces.
  
❯ netstat -nat | grep 9090 | grep -i listen
tcp6       0      0 :::9090                 :::*                    LISTEN         
  
O programa faz o update dos dados a cada 15 segundos. Pra não sobrecarregar.
E ainda falta dar uma melhorada com a entrada como serviço do systemd. Devo fazer isso hoje.
Próximo passo será gerar um pacote debian dele pra instalar fácil.
  Update: Fri Aug 22 04:23:45 PM CEST 2025 
  Tá lá o arquivo pro systemd.
  E está funcionando no sistema que estou testando.
  
root@server:/# systemctl status nginx-openmetrics.service 
● nginx-openmetrics.service - nginx open metrics service
     Loaded: loaded (/etc/systemd/system/nginx-openmetrics.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2025-08-22 14:08:17 UTC; 16min ago
   Main PID: 314061 (nginx-openmetri)
      Tasks: 7 (limit: 19076)
     Memory: 4.0M
        CPU: 47ms
     CGroup: /system.slice/nginx-openmetrics.service
             └─314061 /usr/sbin/nginx-openmetrics --service=http://localhost:8080/api
Aug 22 14:08:17 internal systemd[1]: Started nginx open metrics service.
Aug 22 14:08:17 internal nginx-openmetrics[314061]: [2025-08-22T14:08:17.78433] (INFO): nginx-open-metrics-service (1.0-9)
Aug 22 14:08:17 internal nginx-openmetrics[314061]: [2025-08-22T14:08:17.78441] (INFO): 🚚 fetching data from:http://localhost:8080/api
Aug 22 14:08:17 internal nginx-openmetrics[314061]: [2025-08-22T14:08:17.78442] (INFO): 🎬 starting service at port:9090    
  
    
    
    
 
Confesso que pra escrever o script pra lers os logs do servidor web, aquele que mostrei em acessos de robôs nos logs web, foi algo próximo do vudu. Tudo porque o formato gerado não é lá muito amigável.
Idem pras máquinas do trabalho. Então hoje eu resolvi dar uma olhada se tinha como escrever esses mesmos logs em json.
E tem.
O primeiro que olhei foi no nginx. E é bem fácil de fazer.
  
log_format logger-json escape=json '{"source": "nginx", "time": "$time_iso8601", "resp_body_size": $body_bytes_sent, "host": "$http_host", "address": "$remote_addr", "request_length": $request_length, "method": "$request_method", "uri": "$request_uri", "status": $status,  "user_agent": "$http_user_agent", "resp_time": $request_time, "upstream_addr": "$upstream_addr"}';  
[...]
server {
    listen 443 ssl;
    server_name api.company.com;
    ...
    access_log /var/log/nginx/access.log logger-json;
    ...
}
  
Eu segui a receita desses dois links aqui:
https://www.velebit.ai/blog/nginx-json-logging/
https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format
Pro Apache não tem um módulo que já gera tudo meio mastigado como no nginx. Mas você pode criar o formato do log como quiser.
  
LogFormat "{ \"time\":\"%{%Y-%m-%d}tT%{%T}t.%{msec_frac}tZ\", \"process\":\"%D\", \"filename\":\"%f\", \"remoteIP\":\"%a\", \"host\":\"%V\", \"request\":\"%U\", \"query\":\"%q\", \"method\":\"%m\", \"status\":\"%>s\", \"userAgent\":\"%{User-agent}i\", \"referer\":\"%{Referer}i\" }" combined
ErrorLogFormat "{ \"time\":\"%{%Y-%m-%d}tT%{%T}t.%{msec_frac}tZ\", \"function\" : \"[%-m:%l]\" , \"process\" : \"[pid %P:tid %T]\" , \"message\" : \"%M\" ,\ \"referer\"\ : \"%{Referer}i\" }"    
  
E esse veio de dica do stack-overflow:
Eu não segui os mesmos parâmetros entre apache e nginx. Por enquanto estou só testando e estar com os campos bem definidos já é o suficiente pra mim.
  
> tail -1 /var/log/apache2/linux-br-access.log | jq .
{
  "time": "2025-08-20T09:38:59.369Z",
  "process": "241797",
  "filename": "/var/www/linux-br.org/index.php",
  "remoteIP": "54.36.149.72",
  "host": "linux-br.org",
  "request": "/date/2024/03/page/3/",
  "query": "",
  "method": "GET",
  "status": "200",
  "userAgent": "Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)",
  "referer": "-"
}
  
Agora ficou fácil de filtrar e pegar só os campos que interessam. E usando "jq".
  
> tail -1 /var/log/apache2/linux-br-access.log | jq ".userAgent"
"Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)"
> tail -3 /var/log/apache2/linux-br-access.log | jq ".userAgent"
"Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)"
"Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)"
"Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)"
  
    
    
    
 
Eu tenho um laptop pessoal que é um Thinkpad T480. Escrevi sobre o mesmo aqui: entrei pra moda do laptop refurbished. Como não estou usando ele pra muita coisa e tenho também um Thinkpad pro trabalho, deixo o meu pra rodar o último Ubuntu.
E estava com o oneiric, 24.10, quando tentei fazer upgrade pro plucky, 25.04. E tive um belo dum crash no zfs.
  
------------[ cut here ]------------
WARNING: CPU: 0 PID: 227 at drivers/usb/typec/ucsi/ucsi.c:1390 ucsi_reset_ppm+0x1ad/0x1c0 [typec_ucsi]
Modules linked in: zfs(PO) spl(O) dm_crypt hid_multitouch hid_generic cdc_ncm cdc_ether usbnet uas mii usbhid hid usb_storage crct10dif_pclmul crc32_pclmul polyval_clmulni polyval_generic nvme ghash_clmulni_intel snd sha256_ssse3 soundcore sha1_ssse3 nvme_core e1000e video thunderbolt ucsi_acpi psmouse nvme_auth xhci_pci typec_ucsi typec xhci_pci_renesas sparse_keymap platform_profile wmi aesni_intel crypto_simd cryptd
CPU: 0 UID: 0 PID: 227 Comm: kworker/0:2 Tainted: P           O       6.11.0-25-generic #25-Ubuntu
Tainted: [P]=PROPRIETARY_MODULE, [O]=OOT_MODULE
Hardware name: LENOVO 20L6S4G700/20L6S4G700, BIOS N24ET76W (1.51 ) 02/27/2024
Workqueue: events_long ucsi_init_work [typec_ucsi]
RIP: 0010:ucsi_reset_ppm+0x1ad/0x1c0 [typec_ucsi]
Code: ff 8b 55 bc 81 e2 00 00 00 08 0f 85 33 ff ff ff 4c 89 75 c8 48 8b 05 72 9d 4a cb 49 39 c5 79 94 b8 92 ff ff ff e9 19 ff ff ff <0f> 0b e9 57 ff ff ff e8 17 1b 17 ca 0f 1f 80 00 00 00 00 90 90 90
RSP: 0018:ffffba53c03a3d80 EFLAGS: 00010206
RAX: 0000000008000000 RBX: ffff9d0102192800 RCX: 0000000000000000
RDX: 00000000fffb83c0 RSI: 0000000000000000 RDI: 0000000000000000
RBP: ffffba53c03a3dd0 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: ffffba53c03a3d8c
R13: 00000000fffb83be R14: ffff9d0101a4fc00 R15: ffff9d01021928c0
FS:  0000000000000000(0000) GS:ffff9d0666200000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00005799b59ce675 CR3: 00000003afe3e004 CR4: 00000000003706f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
 <TASK>
 ? show_trace_log_lvl+0x1be/0x310
 ? show_trace_log_lvl+0x1be/0x310
 ? ucsi_init+0x32/0x310 [typec_ucsi]
 ? show_regs.part.0+0x22/0x30
 ? show_regs.cold+0x8/0x10
 ? ucsi_reset_ppm+0x1ad/0x1c0 [typec_ucsi]
 ? __warn.cold+0xa7/0x101
 ? ucsi_reset_ppm+0x1ad/0x1c0 [typec_ucsi]
 ? report_bug+0x114/0x160
 ? handle_bug+0x6e/0xb0
 ? exc_invalid_op+0x18/0x80
 ? asm_exc_invalid_op+0x1b/0x20
 ? ucsi_reset_ppm+0x1ad/0x1c0 [typec_ucsi]
 ucsi_init+0x32/0x310 [typec_ucsi]
 ucsi_init_work+0x18/0x90 [typec_ucsi]
 process_one_work+0x174/0x350
 worker_thread+0x31a/0x450
 ? _raw_spin_lock_irqsave+0xe/0x20
 ? __pfx_worker_thread+0x10/0x10
 kthread+0xe1/0x110
 ? __pfx_kthread+0x10/0x10
 ret_from_fork+0x44/0x70
 ? __pfx_kthread+0x10/0x10
 ret_from_fork_asm+0x1a/0x30 
</TASK>
---[ endtrace 0000000000000000 ]---   
WARNING: CPU: 0 PID: 978 at /build/linux-Ajk80v/linux-6.11.0/debian/build/build-generic/____________________________________________________________________________dkms/build/zfs/2.2.6/build/module/zfs/zfs_log.c:817 zfs_log_setsaxattr+0x140/0x150 [zfs]
Modules linked in: msr(+) parport_pc ppdev lp parport efi_pstore nfnetlink dmi_sysfs ip_tables x_tables autofs4 typec_displayport zfs(PO) spl(O) dm_crypt hid_multitouch hid_generic cdc_ncm cdc_ether usbnet uas mii usbhid hid usb_storage crct10dif_pclmul crc32_pclmul polyval_clmulni polyval_generic nvme ghash_clmulni_intel snd sha256_ssse3 soundcore sha1_ssse3 nvme_core e1000e video thunderbolt ucsi_acpi psmouse nvme_auth xhci_pci typec_ucsi typec xhci_pci_renesas sparse_keymap platform_profile wmi aesni_intel crypto_simd cryptd
CPU: 0 UID: 0 PID: 978 Comm: systemd-random- Tainted: P        W  O       6.11.0-25-generic #25-Ubuntu
Tainted: [P]=PROPRIETARY_MODULE, [W]=WARN, [O]=OOT_MODULE
Hardware name: LENOVO 20L6S4G700/20L6S4G700, BIOS N24ET76W (1.51 ) 02/27/2024
RIP: 0010:zfs_log_setsaxattr+0x140/0x150 [zfs]
Code: ff ff ff 31 c9 48 c7 c2 c0 94 e5 c0 4c 89 f6 4c 89 55 c0 48 c7 c7 68 8e e5 c0 4c 89 4d d0 c6 05 4a 50 13 00 01 e8 a0 c3 a9 c8 <0f> 0b 4c 8b 55 c0 4c 8b 4d d0 e9 30 ff ff ff 90 90 90 90 90 90 90
RSP: 0018:ffffba53c192f748 EFLAGS: 00010246
RAX: 0000000000000000 RBX: ffff9d0107f7bdb0 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
RBP: ffffba53c192f790 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: ffff9d011613e800
R13: ffff9d010b3499c0 R14: 000000000000001c R15: 0000000000000000
FS:  00007a57caef8980(0000) GS:ffff9d0666200000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007a57cbf071e0 CR3: 000000010a016003 CR4: 00000000003706f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
 <TASK>
 ? show_trace_log_lvl+0x1be/0x310
 ? show_trace_log_lvl+0x1be/0x310
 ? zfs_sa_set_xattr+0x34a/0x3b0 [zfs]
 ? show_regs.part.0+0x22/0x30
 ? show_regs.cold+0x8/0x10
 ? zfs_log_setsaxattr+0x140/0x150 [zfs]
 ? __warn.cold+0xa7/0x101
 ? zfs_log_setsaxattr+0x140/0x150 [zfs]
 ? report_bug+0x114/0x160
 ? handle_bug+0x6e/0xb0
 ? exc_invalid_op+0x18/0x80
 ? asm_exc_invalid_op+0x1b/0x20
 ? zfs_log_setsaxattr+0x140/0x150 [zfs]
 ? zfs_log_setsaxattr+0x140/0x150 [zfs]
 zfs_sa_set_xattr+0x34a/0x3b0 [zfs]
 zpl_xattr_set_sa+0x102/0x200 [zfs]
 zpl_xattr_set+0x21c/0x290 [zfs]
 __zpl_xattr_user_set+0x128/0x170 [zfs]
 zpl_xattr_user_set+0x22/0x40 [zfs]
 __vfs_removexattr+0x81/0xd0
 __vfs_removexattr_locked+0xe5/0x1a0
 ? touch_atime+0xbe/0x120  
vfs_removexattr+0x59/0x110
 __do_sys_fremovexattr+0x130/0x1c0
 __x64_sys_fremovexattr+0x15/0x20
 x64_sys_call+0x1fc7/0x22b0
 do_syscall_64+0x7e/0x170
 ? filemap_map_pages+0x34f/0x570
 ? xa_load+0x73/0xb0
 ? do_read_fault+0xfd/0x200
 ? do_fault+0x183/0x210
 ? generic_file_llseek+0x24/0x40
 ? zpl_llseek+0x32/0xd0 [zfs]
 ? ksys_lseek+0x7d/0xd0
 ? syscall_exit_to_user_mode+0x4e/0x250
 ? do_syscall_64+0x8a/0x170
 ? __count_memcg_events+0x86/0x160
 ? count_memcg_events.constprop.0+0x2a/0x50
 ? handle_mm_fault+0x1bb/0x2d0
 ? do_user_addr_fault+0x5e9/0x7e0
 ? irqentry_exit_to_user_mode+0x43/0x250
 ? irqentry_exit+0x43/0x50
 ? exc_page_fault+0x96/0x1c0
 entry_SYSCALL_64_after_hwframe+0x76/0x7e
RIP: 0033:0x7a57cba5d4eb
Code: 73 01 c3 48 8b 0d 0d 79 0e 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa b8 c7 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d dd 78 0e 00 f7 d8 64 89 01 48
RSP: 002b:00007ffe118d0498 EFLAGS: 00000246 ORIG_RAX: 00000000000000c7
RAX: ffffffffffffffda RBX: 0000000000000004 RCX: 00007a57cba5d4eb
RDX: 000000000000001a RSI: 00006015c386c08b RDI: 0000000000000005
RBP: 00007ffe118d05d0 R08: 00007a57cbb45b20 R09: 00000000000000c0
R10: 0000601603619fc0 R11: 0000000000000246 R12: 0000000000000005
R13: 0000000000000001 R14: 0000000000000000 R15: 0000000000000001
 </TASK> 
 
  
    E ficava nisso.
    Tinha de mandar um zfs rollback nos volumes pra conseguir voltar a usar.
    E tentar o upgrade novamente.
  
Depois de muito tentar, resolvi abrir um bug report no launchpad. Meu bug foi marcado como duplicado e passei então a interagir no bug onde o problema foi reportado primeiramente.
tl;dr: o bug era do zfs no kernel padrão que o plucky instala. A correção exige upgrade tanto do zfs quanto do kernel antes de ir pro upgrade do plucky.
Da primeira vez eu errei esse upgrade. E precisei recuperar o zfs pra voltar o snapshot.
E mais um problema já que não existe um procedimento bem descritivo de como fazer isso. Ou tem?
Sim tem. E mais de um.
Tive de recuperar algumas vezes o sistema. Então fiquei meio que craque em fazer isso. O esquema está abaixo:
    
root@ubuntu:~# lsblk -f
NAME        FSTYPE     FSVER            LABEL                    UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
loop0       squashfs   4.0                                                                                  0   100% /rofs
loop1       squashfs   4.0                                                                                           
loop2       squashfs   4.0                                                                                           
loop3       squashfs   4.0                                                                                  0   100% /snap/bare/5
loop4       squashfs   4.0                                                                                  0   100% /snap/core22/1748
loop5       squashfs   4.0                                                                                  0   100% /snap/firefox/5751
loop6       squashfs   4.0                                                                                  0   100% /snap/firmware-updater/167
loop7       squashfs   4.0                                                                                  0   100% /snap/gnome-42-2204/202
loop8       squashfs   4.0                                                                                  0   100% /snap/gtk-common-themes/1535
loop9       squashfs   4.0                                                                                  0   100% /snap/snap-store/1248
loop10      squashfs   4.0                                                                                  0   100% /snap/thunderbird/644
loop11      squashfs   4.0                                                                                  0   100% /snap/ubuntu-desktop-bootstrap/315
loop12      squashfs   4.0                                                                                  0   100% /snap/snapd-desktop-integration/253
loop13      squashfs   4.0                                                                                  0   100% /snap/snapd/23545
sda         iso9660    Joliet Extension Ubuntu 24.04.2 LTS amd64 2025-02-15-09-15-26-00                              
├─sda1      iso9660    Joliet Extension Ubuntu 24.04.2 LTS amd64 2025-02-15-09-15-26-00                     0   100% /cdrom
├─sda2      vfat       FAT12            ESP                      B5A5-8010                                           
├─sda3                                                                                                               
└─sda4      ext4       1.0              writable                 5729a291-83ad-4b15-91b1-09a17bfc9504    1.3G     4% /var/crash
                                                                                                                     /var/log
sdb                                                                                                                  
nvme0n1                                                                                                              
├─nvme0n1p1 vfat       FAT32                                     C399-15AF                                           
├─nvme0n1p2 zfs_member 5000             bpool                    4626876014803904226                                 
├─nvme0n1p3                                                                                                          
└─nvme0n1p4 zfs_member 5000             rpool                    15334588309526604034                                
root@ubuntu:~# zpool import -f rpool
root@ubuntu:~# zpool list
NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
rpool   936G   365G   571G        -         -     6%    39%  1.00x    ONLINE  -
root@ubuntu:~# cryptsetup open /dev/zvol/rpool/keystore rpool-keystore
Enter passphrase for /dev/zvol/rpool/keystore: 
root@ubuntu:~# mkdir /mnt-keystore
root@ubuntu:~# mount /dev/mapper/rpool-keystore /mnt-keystore
root@ubuntu:~# ls /mnt-keystore
lost+found  system.key
root@ubuntu:~# cat /mnt-keystore/system.key | zfs load-key -L prompt rpool
root@ubuntu:~# umount /mnt-keystore 
root@ubuntu:~# cryptsetup close rpool-keystore
root@ubuntu:~# zfs list
NAME                                               USED  AVAIL  REFER  MOUNTPOINT
rpool                                              365G   542G   192K  /
rpool/ROOT                                         106G   542G   192K  none
rpool/ROOT/ubuntu_ni6nkv                           106G   542G  11.5G  /mnt
rpool/ROOT/ubuntu_ni6nkv/srv                       352K   542G   192K  /mnt/srv
rpool/ROOT/ubuntu_ni6nkv/usr                      7.96M   542G   192K  /mnt/usr
rpool/ROOT/ubuntu_ni6nkv/usr/local                7.77M   542G  6.02M  /mnt/usr/local
rpool/ROOT/ubuntu_ni6nkv/var                      64.4G   542G   192K  /mnt/var
rpool/ROOT/ubuntu_ni6nkv/var/games                 272K   542G   192K  /mnt/var/games
rpool/ROOT/ubuntu_ni6nkv/var/lib                  60.0G   542G  24.2G  /mnt/var/lib
rpool/ROOT/ubuntu_ni6nkv/var/lib/AccountsService  1.07M   542G   212K  /mnt/var/lib/AccountsService
rpool/ROOT/ubuntu_ni6nkv/var/lib/NetworkManager   7.59M   542G   580K  /mnt/var/lib/NetworkManager
rpool/ROOT/ubuntu_ni6nkv/var/lib/apt               388M   542G   103M  /mnt/var/lib/apt
rpool/ROOT/ubuntu_ni6nkv/var/lib/dpkg             1.14G   542G   169M  /mnt/var/lib/dpkg
rpool/ROOT/ubuntu_ni6nkv/var/log                   261M   542G  92.7M  /mnt/var/log
rpool/ROOT/ubuntu_ni6nkv/var/mail                  272K   542G   192K  /mnt/var/mail
rpool/ROOT/ubuntu_ni6nkv/var/snap                 4.11G   542G  4.03G  /mnt/var/snap
rpool/ROOT/ubuntu_ni6nkv/var/spool                10.6M   542G   468K  /mnt/var/spool
rpool/ROOT/ubuntu_ni6nkv/var/www                  55.3M   542G  55.1M  /mnt/var/www
rpool/USERDATA                                     258G   542G   192K  none
rpool/USERDATA/home_39e1h7                         258G   542G   242G  /home
rpool/USERDATA/root_39e1h7                        58.8M   542G  28.4M  /root
rpool/keystore                                    39.8M   542G  16.5M  -
rpool/var                                          739M   542G   192K  /var
rpool/var/lib                                      739M   542G   192K  /var/lib
rpool/var/lib/docker                               738M   542G   729M  /var/lib/docker
root@ubuntu:~# zfs set mountpoint=/mnt rpool/ROOT/ubuntu_ni6nkv
root@ubuntu:~# zfs mount rpool/ROOT/ubuntu_ni6nkv
root@ubuntu:~# ls /mnt
bin  boot  cdrom  dev  etc  home  lib  lib32  lib64  media  mnt  opt  proc  root  run  sbin  snap  srv  sys  tmp  usr  var
root@ubuntu:~# zpool import -N -R /mnt bpool
root@ubuntu:~# zfs mount bpool/BOOT/ubuntu_ni6nkv
root@ubuntu:~# ls /mnt/boot/
System.map-6.11.0-21-generic  config-6.11.0-21-generic  efi         initrd.img-6.11.0-21-generic  initrd.img.old      memtest86+x64.bin  vmlinuz-6.11.0-21-generic  vmlinuz.old
System.map-6.11.0-24-generic  config-6.11.0-24-generic  grub        initrd.img-6.11.0-24-generic  memtest86+ia32.bin  memtest86+x64.efi  vmlinuz-6.11.0-24-generic
System.map-6.14.0-15-generic  config-6.14.0-15-generic  initrd.img  initrd.img-6.14.0-15-generic  memtest86+ia32.efi  vmlinuz            vmlinuz-6.14.0-15-generic
root@ubuntu:~# mount /dev/nvme0n1p1 /mnt/boot/efi/
root@ubuntu:~# ls /mnt/boot/efi/
EFI
root@ubuntu:~# for i in proc dev sys dev/pts; do mount -v --bind /$i /mnt/$i; done
mount: /proc bound on /mnt/proc.
mount: /dev bound on /mnt/dev.
mount: /sys bound on /mnt/sys.
mount: /dev/pts bound on /mnt/dev/pts.
root@ubuntu:~# zfs set mountpoint=/ rpool/ROOT/ubuntu_ni6nkv
Broadcast message from systemd-journald@ubuntu (Sat 2025-04-19 15:35:17 UTC):
systemd[1]: Caught , from our own process.
root@ubuntu:~# zfs list
NAME                                               USED  AVAIL  REFER  MOUNTPOINT
bpool                                              838M   953M    96K  /mnt/boot
bpool/BOOT                                         833M   953M    96K  none
bpool/BOOT/ubuntu_ni6nkv                           833M   953M   295M  /mnt/boot
rpool                                              365G   542G   192K  /
rpool/ROOT                                         106G   542G   192K  none
rpool/ROOT/ubuntu_ni6nkv                           106G   542G  11.5G  /
rpool/ROOT/ubuntu_ni6nkv/srv                       352K   542G   192K  /srv
rpool/ROOT/ubuntu_ni6nkv/usr                      7.96M   542G   192K  /usr
rpool/ROOT/ubuntu_ni6nkv/usr/local                7.77M   542G  6.02M  /usr/local
rpool/ROOT/ubuntu_ni6nkv/var                      64.4G   542G   192K  /var
rpool/ROOT/ubuntu_ni6nkv/var/games                 272K   542G   192K  /var/games
rpool/ROOT/ubuntu_ni6nkv/var/lib                  60.0G   542G  24.2G  /var/lib
rpool/ROOT/ubuntu_ni6nkv/var/lib/AccountsService  1.07M   542G   212K  /var/lib/AccountsService
rpool/ROOT/ubuntu_ni6nkv/var/lib/NetworkManager   7.59M   542G   580K  /var/lib/NetworkManager
rpool/ROOT/ubuntu_ni6nkv/var/lib/apt               388M   542G   103M  /var/lib/apt
rpool/ROOT/ubuntu_ni6nkv/var/lib/dpkg             1.14G   542G   169M  /var/lib/dpkg
rpool/ROOT/ubuntu_ni6nkv/var/log                   261M   542G  92.7M  /var/log
rpool/ROOT/ubuntu_ni6nkv/var/mail                  272K   542G   192K  /var/mail
rpool/ROOT/ubuntu_ni6nkv/var/snap                 4.11G   542G  4.03G  /var/snap
rpool/ROOT/ubuntu_ni6nkv/var/spool                10.6M   542G   468K  /var/spool
rpool/ROOT/ubuntu_ni6nkv/var/www                  55.3M   542G  55.1M  /var/www
rpool/USERDATA                                     258G   542G   192K  none
rpool/USERDATA/home_39e1h7                         258G   542G   242G  /home
rpool/USERDATA/root_39e1h7                        58.8M   542G  28.4M  /root
rpool/keystore                                    39.8M   542G  16.5M  -
rpool/var                                          739M   542G   192K  /var
rpool/var/lib                                      739M   542G   192K  /var/lib
rpool/var/lib/docker                               738M   542G   729M  /var/lib/docker
root@ubuntu:~# zfs set mountpoint=/boot bpool
root@ubuntu:~# zfs list
NAME                                               USED  AVAIL  REFER  MOUNTPOINT
bpool                                              838M   953M    96K  /mnt/boot
bpool/BOOT                                         833M   953M    96K  none
bpool/BOOT/ubuntu_ni6nkv                           833M   953M   295M  /mnt/boot
rpool                                              365G   542G   192K  /
rpool/ROOT                                         106G   542G   192K  none
rpool/ROOT/ubuntu_ni6nkv                           106G   542G  11.5G  /
rpool/ROOT/ubuntu_ni6nkv/srv                       352K   542G   192K  /srv
rpool/ROOT/ubuntu_ni6nkv/usr                      7.96M   542G   192K  /usr
rpool/ROOT/ubuntu_ni6nkv/usr/local                7.77M   542G  6.02M  /usr/local
rpool/ROOT/ubuntu_ni6nkv/var                      64.4G   542G   192K  /var
rpool/ROOT/ubuntu_ni6nkv/var/games                 272K   542G   192K  /var/games
rpool/ROOT/ubuntu_ni6nkv/var/lib                  60.0G   542G  24.2G  /var/lib
rpool/ROOT/ubuntu_ni6nkv/var/lib/AccountsService  1.07M   542G   212K  /var/lib/AccountsService
rpool/ROOT/ubuntu_ni6nkv/var/lib/NetworkManager   7.59M   542G   580K  /var/lib/NetworkManager
rpool/ROOT/ubuntu_ni6nkv/var/lib/apt               388M   542G   103M  /var/lib/apt
rpool/ROOT/ubuntu_ni6nkv/var/lib/dpkg             1.14G   542G   169M  /var/lib/dpkg
rpool/ROOT/ubuntu_ni6nkv/var/log                   261M   542G  92.7M  /var/log
rpool/ROOT/ubuntu_ni6nkv/var/mail                  272K   542G   192K  /var/mail
rpool/ROOT/ubuntu_ni6nkv/var/snap                 4.11G   542G  4.03G  /var/snap
rpool/ROOT/ubuntu_ni6nkv/var/spool                10.6M   542G   468K  /var/spool
rpool/ROOT/ubuntu_ni6nkv/var/www                  55.3M   542G  55.1M  /var/www
rpool/USERDATA                                     258G   542G   192K  none
rpool/USERDATA/home_39e1h7                         258G   542G   242G  /home
rpool/USERDATA/root_39e1h7                        58.8M   542G  28.4M  /root
rpool/keystore                                    39.8M   542G  16.5M  -
rpool/var                                          739M   542G   192K  /var
rpool/var/lib                                      739M   542G   192K  /var/lib
rpool/var/lib/docker                               738M   542G   729M  /var/lib/docker      
     
  
  No fim deu certo e consegui fazer upgrade pro plucky. Mas o problema ainda existe. Não sei se será um problema quando chegar a época de upgrade do 24.04.
Espero que não.
Esse é um dos vídeos mais hilários que vi aqui na Suécia. Pra encurtar a história, aqui tinha um carrinho de cachorros quentes que servia uma salsicha super apimentada. Era chamado de harakiri, do termo japonês pra suicídio, e tornou-se um trend aqui por um tempo.
O vídeo funciona com legendas em inglês. Então é possível assistir e gargalhar gostoso com a reportagem. Era mais apimentado que spray de pimenta que a polícia usa.
E eu já tentei essa iguaria?
Não.
E não vou nunca mais nem tentar porque o dono do carrinho parou de servir. Não que isso mudasse minha opinião sobre não tentar. Mas ele achou que estava chamando muita a atenção e tirando sua privacidade. Vendeu tudo e foi vender hambúrgueres em Malmö.
Update: eu vi que o vocêtubo não está funcionando corretamente nos telefones. Então aqui embaixo está o link pra vídeo.
Page 1 of 36