2023年11月23日木曜日

Gitlab の Alertmanager で特定のアラートだけメールで送信する方法

Gitlab の Alertmanager で特定のアラートだけメールで送信する方法

概要

前回はとりあえず出たアラートをすべてメールで送信しました
今回は route 機能を使って特定のアラートだけをメールで飛ばすようにしてみます

環境

  • Gitlab 16.3.6
  • Alertmanager 0.25.0

gitlab.rb

alertmanager['listen_address'] = '0.0.0.0:9093'
alertmanager['receivers'] = [
  {
    name: 'email',
    email_configs: [
      to: 'your_to@mail',
      from: 'your_from@mail',
      smarthost: 'smtp.ess.nifcloud.com:465',
      auth_username: 'your_access_key',
      auth_password: 'your_secret_key',
      require_tls: false,
      send_resolved: true
    ]
  }
]
alertmanager['routes'] = [
  {
    receiver: 'email',
    group_wait: '30s',
    group_interval: '5m',
    repeat_interval: '4h',
    matchers: [ 'alertname = RedisDown' ]
  }
]
# alertmanager['default_receiver'] = 'email'

ポイント

  • alertname は必ず付与されるのでこれを使ってアラートを特定します
    • マッチには正規表現が使える match_re などもあります
    • ルールをカスタムすれば severity などのラベルも付与できますが面倒なので alertname を使います
    • デフォルトの Gitlab のルールだと他に instance, job あたりがマッチルールに使えます
  • default_receiver を設定してしまうと route にマッチしない場合に defualt_receiver の設定を使ってしまうのでコメントする

参考サイト

2023年11月22日水曜日

BeautifulSoup を使った画像URL取得のサンプルコード

BeautifulSoup を使った画像URL取得のサンプルコード

概要

img タグを取得してその src 属性を取得するだけです

環境

  • macOS 14.0
  • Python 3.11.6

サンプルコード

import requests
from bs4 import BeautifulSoup


class Downloader:
    def __init__(self, url: str = "https://picsum.photos/"):
        super().__init__()
        self.url = url

    def get_img_urls(self) -> list:
        response = None
        try:
            with requests.Session() as session:
                response = session.get(self.url, timeout=60)
                if response.status_code == 200:
                    soup = BeautifulSoup(response.text, "html.parser")
                    img_urls = [element.get("src") for element in soup.find_all("img")]
                    return img_urls
        except Exception as e:
            print(e)
            if response:
                response.close()
        return []


if __name__ == "__main__":
    dl = Downloader()
    for img_url in dl.get_img_urls():
        print(img_url)

2023年11月21日火曜日

Chromeでパスワードを一括削除する方法

Chromeでパスワードを一括削除する方法

概要

最近の chrome だと履歴から削除できなくなっています
とりあえずローカルだけでも削除したい場合には以下の方法が簡単です

環境

  • Windows 10
  • Chrome 119.0.6045.160

同期をオフにする

Chrome で以下にアクセスし同期設定でパスワードだけ無効にします

chrome://settings/syncSetup/advanced

Login Data ファイルの削除

%LocalAppData%\Google\Chrome\User Data\Default\ にある Login Data を削除します

動作確認

Chrome を再起動してパスワードが補完されないことを確認しましょう

最後に

根本的には Google のパスワードマネージャに保存されているデータを削除する必要がありますがなぜかパスワードマネージャはパスワードを一件ずつしか削除できないのでこの方法で無理やりローカルだけ削除しています

ちなみに同期を有効にしてしまうと再度ローカルにパスワードファイルが生成されてしまいます

また執筆時点での Chrome ではパスワードのオートフィルを無効にする機能がないのでパスワード自体を削除しないと勝手に補完してしまいます

参考サイト

2023年11月20日月曜日

Gitlab の Alertmanager を使ってメール通知する方法

Gitlab の Alertmanager を使ってメール通知する方法

概要

過去に slack 通知する方法を紹介しました
今回はメール通知する方法を紹介します

環境

  • Gitlab 16.3.6

gitlab.rb の設定

alertmanager['listen_address'] = '0.0.0.0:9093'
alertmanager['receivers'] = [
  {
    name: 'email',
    email_configs: [
      to: 'your_to@mail',
      from: 'your_from@mail',
      smarthost: 'smtp.ess.nifcloud.com:465',
      auth_username: 'your_access_key',
      auth_password: 'your_secret_key',
      require_tls: false,
      send_resolved: true
    ]
  }
]
alertmanager['default_receiver'] = 'email'

docker-compose.yml の設定

9093 を外部から見れるようにするには ports を設定してホスト側にもバインドしてあげましょう

ports:
  - 9093:9093

動作確認

  • docker-compose down
  • docker-compose up -d

で適用し

  • docker-compose exec gitlab gitlab-ctl stop redis

すると以下のようなメールが来ることが確認できると思います

Alertmanager でもアラートを確認できると思います

トラブルシューティング

2023-11-17_04:52:18.74549 ts=2023-11-17T04:52:18.745Z caller=notify.go:732 level=warn component=dispatcher receiver=email integration=email[0] msg="Notify attempt failed, will retry later" attempts=1 err="'require_tls' is true (default) but \"smtp.ess.nifcloud.com:465\" does not advertise the STARTTLS extension"

こんな感じのエラーが Alertmangaer から出ている場合は require_tls を false にしてください


An error has occurred while serving metrics:

collected metric pg_replication_is_replica label:{name:"server"  value:"/var/opt/gitlab/postgresql:5432"}  gauge:{value:0} has help "Indicates if this host is a slave" but should have "Indicates if the server is a replica"

という感じで postgres_exporter がエラーになっている場合は postgres_exporter を無効にすればとりあえずアラートは解消されます

postgres_exporter['enable'] = false

参考サイト

2023年11月18日土曜日

PydanticV2 で dict から dataclass を生成する方法

PydanticV2 で dict から dataclass を生成する方法

概要

dict -> obj のデシリアライズを Pydantic V2 を使ってやる方法を紹介します
pydantic の dataclass を使います

過去に JSONWizard を使った方法も紹介しています

環境

  • Python 3.10.2
  • pydantic 2.5.1

サンプルコード

from dataclasses import asdict

from pydantic.dataclasses import dataclass


@dataclass
class Profile:
    lang: str
    framework: str


@dataclass
class User:
    id: int
    name: str
    profile: Profile


if __name__ == "__main__":
    # dict -> obj
    user_dict = {
        "id": 1,
        "name": "hawksnowlog",
        "profile": {
            "lang": "python",
            "framework": "flask",
        },
    }
    user = User(**user_dict)
    print(user.id)
    print(user.name)
    print(user.profile.lang)
    print(user.profile.framework)
    # obj -> dict
    print(asdict(user))

ちょっと解説

  • ネストした構造でもいけます
  • Optional など組み合わせれば任意の値もとれます
  • BaseModel ではこの方法は使えません
  • 各種フィールドは Field として定義することはできません

最後に

BaseModel として使えないのが少し残念ですが単純な dict との変換ならこれが一番簡単な方法かなと思います

ただサードパーティを使うので管理などは少し面倒です
本当に簡単な変換なのであれば純正の json モジュールだけでも何とかなります

参考サイト

2023年11月17日金曜日

rqscheduler でエンキューする際にキーワード引数を指定するサンプル

rqscheduler でエンキューする際にキーワード引数を指定するサンプル

概要

kwargs を使ってワーカー側は受け取ることができるので特定の enqueue_in を判定するときなどに使えそうです

環境

  • Python 3.11.3
  • rq-scheduler 0.13.1

サンプルコード

from datetime import timedelta

from redis import Redis
from rq import Queue
from rq_scheduler import Scheduler

from my_lib.util import Message

queue = Queue("default", connection=Redis())
scheduler = Scheduler(queue=queue, connection=queue.connection)


scheduler.enqueue_in(timedelta(seconds=5), Message().say, msg="hello")
  • vim my_lib/util.py
class Message:
    def say(self, **kwargs):
        print(kwargs)

最後に

AttributeError: Can't get attribute 'Message' on <module '__main__' from '/home/user01/.local/share/virtualenvs/python-T6UYzsfV/bin/rqscheduler'>

という感じのエラーになる場合は enqueue_in する際のメソッドを素直にモジュール化しましょう
今回であれば my_lib/__init__.py の設置も忘れずに行いましょう

2023年11月16日木曜日

Pydantic で Union を使って model_validate する場合は必ず discriminator を使うべきである

Pydantic で Union を使って model_validate する場合は必ず discriminator を使うべきである

概要

model_validate を使用すると pydantic が自動で型を推論します
更に Union を使っている場合は Union に指定している複数のクラスのどれにするかを自動的に推論します
同じフィールドを持つ場合には意図しないクラスに割り当てられることもあります
そういったことを避けるために discriminator という機能を使いましょう

環境

  • Python 3.10.2
  • pydantic 2.5.1

サンプルコード

from typing import Annotated, Literal, Union

from pydantic import BaseModel, Field, field_validator


class Cat(BaseModel):
    # model_validate 時の型推論用フィールド
    animal_type: Literal["cat"] = "cat"
    value: str = Field()

    @field_validator("value")
    @classmethod
    def validate_value(cls, v: str):
        if v != "neko":
            raise ValueError()
        return v


class Dog(BaseModel):
    # model_validate 時の型推論用フィールド
    animal_type: Literal["dog"] = "dog"
    value: str = Field()

    @field_validator("value")
    @classmethod
    def validate_value(cls, v: str):
        if v != "inu":
            raise ValueError()
        return v


Animal = Annotated[
    Union[Cat, Dog],
    Field(discriminator="animal_type"),
]


class Trainer(BaseModel):
    # model_validate 時の型推論用フィールド
    animal: Animal = Field()


if __name__ == "__main__":
    # 成功
    cat = Cat(value="neko")
    dog = Dog(value="inu")
    trainer1 = Trainer(animal=cat)
    trainer2 = Trainer(animal=cat)
    Trainer.model_validate(trainer1.model_dump())
    Trainer.model_validate(trainer2.model_dump())
    # 失敗
    cat = Cat(value="inu")
    trainer1 = Trainer(animal=cat)
    Trainer.model_validate(trainer1.model_dump())

ちょっと解説

一見当たり前のことをしていますが同一のフィールド名をもつクラスを作成しそのクラスに対して model_validate を実行するとフィールドの情報からクラスを推測するため間違ったクラスになる可能性があります

model_dump はクラスの json 情報を返却します
また model_validate で渡す json 情報はすべてのフィールドがプリミティブな値でなければいけないので model_dump される json はすべて文字列だけの情報になります

その状態で discriminator 情報なしで model_validate するとクラスを特定する情報がない状態になってしまいます
なのでクラスを特定する情報として今回 animal_type フィールドを追加し必ずクラスごとに一意になるように値を設定することでプリミティブな値のみを持つ model_dump の情報からでも適切なクラスに変換し model_validate することができるようになります

最後に

Pydantic で Union を使う場合には必ず discriminator を使うようにしましょう

参考サイト