nginx-quicパッケージをquictlsで作り直した

以前作成したnginx-quicパッケージをquictls版OpenSSLを使うように作り直しました。https://github.com/ryoh/nginx-quic.spec

ryoh-k.hateblo.jp

基本的なコードなどは変わりませんが、以前はBoringSSLでしか対応ができなかったのが、quictls版OpenSSLでも対応できるようになったため、cmakeが必要なくなりビルドが容易になりました。(併せてnginxも最新版に更新)

またOpenSSL 3.0に変えたため kTLS(Kernel TLS)が使えるようになりました。最大で30%程度性能改善がするようです。nginx.conf 周りも更新しているので一応そのまま使えれる状態にしています。

www.nginx.co.jp

そのほかzstdモジュールにも対応してほしいissueが来ていたので対応しています。が、どうなんでしょうかね。

最後にSPECファイルの抜粋となります。 ビルド方法はOpenSSLをスタティックリンクする方法と同じで違いは特にありません。 nginx-quicのビルドオプションもそのまま使用できます。

%global         nginx_quic_commit   cd8018bc81a5
%global         quictls_version     3.0.2

Name:           nginx-quic
Version:        1.21.6
Release:        4%{?dist}.quictls%{quictls_version}
Summary:        A high performance web server and reverse proxy
Group:          System Environment/Daemons 
License:        BSD
URL:            https://nginx.org/
Source0:        https://hg.nginx.org/nginx-quic/archive/%{nginx_quic_commit}.tar.gz#/nginx-quic-%{nginx_quic_commit}.tar.gz
Source100:      https://github.com/quictls/openssl/archive/openssl-%{quictls_version}+quic.tar.gz

%description
nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server,
and a generic TCP/UDP proxy server, originally written by Igor Sysoev.

%prep
%setup -q -n %{name}-%{nginx_quic_commit}

pushd ..
MODULE="quictls"
%{__rm} -rf ${MODULE}
%{__mkdir} ${MODULE}
cd ${MODULE}
%{__tar} -xf %{SOURCE100} --strip 1
popd

%build
./auto/configure \
  --with-debug \
  --with-openssl=../quictls \
  --with-openssl-opt="enable-ktls" \
  --with-http_v3_module \
  --with-stream_quic_module \

%make_build

%install
[[ -d %{buildroot} ]] && rm -rf "%{buildroot}"
%{__mkdir} -p "%{buildroot}"
%make_install INSTALLDIRS=vendor

unlink %{buildroot}/etc/nginx/koi-utf
unlink %{buildroot}/etc/nginx/koi-win
unlink %{buildroot}/etc/nginx/win-utf

%files
%defattr(-,root,root)
/usr/sbin/nginx
%config(noreplace) /etc/nginx/nginx.conf
%config(noreplace) /etc/nginx/mime.types
%config(noreplace) /etc/nginx/fastcgi_params
%config(noreplace) /etc/nginx/scgi_params
%config(noreplace) /etc/nginx/uwsgi_params

%changelog

RPMパッケージは以前と同じCoprで公開中です。https://copr.fedorainfracloud.org/coprs/ryoh/nginx-quic/

QUIC対応版nginxが出ていたのでビルドとRPMパッケージを作成した。

www.nginx.co.jp

先月くらいにnginxのQUIC preview版が出ていましたので今更ながら試してみました。

coprで作ったのでお試しください。(既存のnginxを上書きするのでご注意ください。)

https://copr.fedorainfracloud.org/coprs/ryoh/nginx-quic/

まずnginxをビルドしなければいけませんが、そのあたりはこちらの方の記事の通りに行うだけでできました。

asnokaze.hatenablog.com

今回は他の環境でも使いやすいようRPMパッケージを作成します。

RPMパッケージ作成についてですが、基本は記事の通りBoringSSLとnginx-quicを使ってビルドするだけです。

ただ、パッケージを作るタイミングでファイルの状態が変わると安定性がないため、HEADからではなくハッシュ値からファイルを取得します。

nginxもBoringSSLもMercurialなので下記のような形でファイルを取得します。

https://hg.nginx.org/nginx-quic/archive/コミットのハッシュ値.tar.gz

ここ以外はあまり変わらず、こんな感じでSPECファイルを作成してRPMパッケージを作るだけです。
(詳細は下記僕のレポジトリをご参照ください。)

https://github.com/ryoh/nginx-quic.spec

Name:           nginx-quic
Version:        1.19.1
Release:        0%{?dist}
Summary:        A high performance web server and reverse proxy
Group:          System Environment/Daemons 
License:        BSD
URL:            https://nginx.org/

Source0:        https://hg.nginx.org/nginx-quic/archive/e533a352d118.tar.gz
Source100:      https://boringssl.googlesource.com/boringssl/+archive/54858b63c1d886f6c8d903d4a4f594f1485de189.tar.gz

%description
nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server,
and a generic TCP/UDP proxy server, originally written by Igor Sysoev.

%prep
%setup -q -n %{name}-e533a352d118

pushd ..
%{__rm} -rf boringssl
%{__mkdir} boringssl
cd boringssl
%{__tar} -xf %{SOURCE100}
popd

%build
pushd ../boringssl
mkdir build
cd build
cmake3 -GNinja ..
ninja

cd ..
mkdir -p .openssl/lib
cd .openssl
ln -s ../include .
cd ..
cp build/crypto/libcrypto.a build/ssl/libssl.a .openssl/lib
popd

./auto/configure \
  --with-debug \
  --with-cc-opt="-I../boringssl/include" \
  --with-ld-opt="-L../boringssl/build/ssl -L../boringssl/build/crypto" \
  --with-http_v3_module \

%make_build

%install
[[ -d %{buildroot} ]] && rm -rf "%{buildroot}"
%{__mkdir} -p "%{buildroot}"
%make_install INSTALLDIRS=vendor

unlink %{buildroot}/etc/nginx/koi-utf
unlink %{buildroot}/etc/nginx/koi-win
unlink %{buildroot}/etc/nginx/win-utf

%files
%defattr(-,root,root)
/usr/sbin/nginx

%config(noreplace) /etc/nginx/nginx.conf
%config(noreplace) /etc/nginx/mime.types
%config(noreplace) /etc/nginx/fastcgi_params
%config(noreplace) /etc/nginx/scgi_params
%config(noreplace) /etc/nginx/uwsgi_params

%changelog

あとはこのパッケージに対してファイルを取得して、RPMパッケージをビルドするだけです。

spectool -g -R SPECS/nginx-quic.spec
rpmbuild -ba SPECS/nginx-quic.spec

作成したRPMパッケージを他の環境などに持っていきyumコマンドでインストールすれば利用可能です。

yum install RPMS/x86_64/nginx-quic-1.19.1-1.el7.rpm

SRPMをCOPRというサービスに持っていけば、簡易な自分のレポジトリができます。(UbuntuのPPAのような)

https://copr.fedorainfracloud.org/coprs/ryoh/nginx-quic/

yum copr enable ryoh/nginx-quic
yum install nginx-quic

最近だとDockerで作ったほうが簡単そうですが、Redhat, CentOSに限ればこういう配布方法もあるよ、という話でした。

Zabbix API で Rust をお試し

公式ドキュメントなどでなんとなく見てたけど、 実際に作ったほうが使い方わかるので、Zabbix API を叩くツールを作りながら勉強

やること

Zabbix API ドキュメント見ると "user.login"メソッドで認証しトークンを取得してからじゃないと
各種操作ができないようなので、とりあえず以下までを目標とします。

  1. ZABBIXサーバーにログインする
  2. ホスト一覧を取得

Zabbix APIJSON RPCでやり取りするため、JSONとHTTPリクエストが扱えるcrateを使います。

JSONserde、HTTPリクエストはreqwestが代表的だったのでそれを使用。

Cargo.tomlはこんな感じ

[dependencies]
reqwest = { version = "0.10", features = ["json"] }
tokio = { version = "0.2", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

実装

ユーザーログイン

とりあえずログインするまでだったらこんな感じですね。

use serde_json::json;

// reqwestが非同期のためasyncにしておく
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // ZABBIXサーバーのAPIエンドポイントの場所
    let url = "http://my.zabbix.example.com/api_jsonrpc.php";
    // ZABBIX側で作成したユーザー名
    let user = "api";
    // ZABBIX側で作成したユーザーのパスワード
    let password = "password";

    // JSON-RPCリクエストオブジェクト(idは任意、authはなし)
    let request = json!({
        jsonrpc: "2.0",
        method: "user.login",
        params: {
            "user": user,
            "password": password
        },
        id: 1,
        auth: null
    });

    // reqwest クライアントオブジェクトを生成(postでリクエストを送る必要があるため)
    let client = reqwest::Client::new();

    // ZABBIXサーバーにJSON RPCリクエストをPOSTする。
    // 正常に疎通できればレスポンスを取得できる(reqwestが標準だと非同期なためawaitで待つ)
    let response = client.post(url)
                         .json(&request)
                         .send()
                         .await?;

    // レスポンスボディをJSON形式に直した状態で抽出(serdeでDeserializeできる型ならOK)
    let content: serde_json::Value = response.json().await?;

    // レスポンスの値を表示
    // sample:
    // {
    //     "jsonrpc": "2.0",
    //     "result": "0424bd59b807674191e7d77572075f33",
    //     "id": 1
    // }
    println!("{:#?}", content);

    // --- 本来はレスポンスのidが一致するか、エラーが無かったか確認する ---

    Ok(())
}

実際にはこのあと content の中のトークンをリクエストのauthにセットして送ることで、各種情報の取得やホストなど監視設定の作成を行うことになる。

次にZABBIXに登録されているホストの一覧を出しましょう。

ホスト一覧の取得

ZABBIXのホスト一覧メソッドは"host.get"となっている。

host.get API

リクエストの形式はこんな形。

{
    "jsonrpc": "2.0",
    "method": "host.get",
    "params": {
        "output": [
            "hostid",
            "host"
        ],
        "selectGroups": "extend",
        "selectInterfaces": "extend",
        "search": {
            "host": "server"
        }
    },
    "auth": "038e1d7b1735c6a5436ee9eae095879e",
    "id": 2
}

paramsは抽出条件や出力内容をを指定するパラメータ。結構細かく指定できるので詳細は公式ドキュメントを見てもらったほうが良い。

上の場合だと、名前に"server"を含むホストのHostIdとホスト名すべてを取得し、
割り当てられているホストグループとインターフェースを合わせて出力するという内容になる。

さて、コードにすると以下の感じになる。(始めのトークン取得まではそのままなので以降を記載する)

use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let url = "http://my.zabbix.example.com/api_jsonrpc.php";
    let client = reqwest::Client::new();

    // ・・・先程の、"user.login"処理を行い、トークンを取得する。・・・

    // トークンを取得
    let token = content["result"].as_str().expect("Can not parse token");

    // "host.get"のリクエストを作成
    let request = json!({
        "jsonrpc": "2.0",
        "method": "host.get",
        "params": {
            "output": [
                "hostid",
                "host"
            ],
            "selectGroups": "extend",
            "selectInterfaces": "extend",
            "search": {
                "host": "server"
            }
        },
        "auth": token,
        "id": 2
    });

    // POSTの送信からレスポンス取得、JSON型への変換を一律で行う方法。
    let content: serde_json::Value = client.post(url)
                                           .json(&request)
                                           .send()
                                           .await?
                                           .json()
                                           .await?;

    // 抽出結果を取得と表示
    let hosts = content.as_array().expect("Can not parse array");
    println!("{:#}", hosts);

    Ok(())
}

一応これでcargo runとかで確認可能

このあとはもう少しシンプルに作るために処理をまとめたり型作ったりする予定

同期・非同期のreqwestの使い方

この記事の目的

Zabbix APIを叩く簡単なCLIツールを作ろうと思ったけど、リクエスト作るだけで結構苦労したので次使うときのためにメモ。

※reqwest 0.10.4 を対象

参考

導入

Cargo.tomlの[dependences]にcrateを追加

[dependences]
reqwest = { version = "0.10", features = ["json"] }
tokio = { version = "0.2", features = ["full"] }

ブロッキングで接続する場合は以下の内容にする

[dependences]
reqwest = { version = "0.10", features = ["blocking", "json"] }

GET

基本的な書き方(デフォルト非同期)

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resp = reqwest::get("https://rust-lang.org")
        .await?
        .text()
        .await?;
    println!("{:#?}", resp);
    Ok(())
}

ブロッキングでの書き方

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resp = reqwest::blocking::get("https://rust-lang.org").text()?;
    println!("{:#?}", resp);
    Ok(())
}

POST

まずはClientを作成してからPOST

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = "example data";

    let client = reqwest::Client::new();
    let resp = client.post("http://example.com/")
        .body(&data)
        .send()?
        .await?;
    let result = resp.text().await?;

    println!("{:#?}", result);
    Ok(())
}

ブロッキング

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = "example data";

    let client = reqwest::blocking::Client::new();
    let resp = client.post("http://example.com/").body(&data).send()?;
    let result = resp.text()?;

    println!("{:#?}", result);
    Ok(())
}

とりあえずここまで

Unity 独自クラスをScritableObjectで編集できるようにする。

ValueObjectライクにクラスを使いたかったので、フィールドを独自クラスにしたかった。

だけど、独自クラスだとインスペクタから値を変えれなかった。 調べると"[Serializable]"を使えばできるようなのでメモ。

下記のように独自クラスに"[System.Serializable]"を割り当てて さらに中のフィールドに"[SerializeField]"かpublicにすれば独自クラスもインスペクタから編集できるようになる。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class MyClass
{
    [SerializeField] private int id = 0;
    [SerializeField] private string name = "";

    public int Id { get; }
    public int Name { get; }

    public MyClass(MyClass object)
    {
        Id = object.Id;
        Name = object.Name;
    }
}

Unity UIを等間隔に配置する。

以下の動画で勉強してたけどUIを等間隔で配置する方法わからなくなったのでメモ

www.youtube.com

Horizontal Layout Group とりあえずこれを使うことというのは覚えた。 詳細はほかサイトで確認。