zaki work log

作業ログやら生活ログやらなんやら

Docker版NetBoxを導入してIPアドレス管理台帳.xlsxに別れを告げる

オンプレ環境だと必ずと言っていいほど存在する「エクセルのIPアドレス管理台帳」、なんやかやで私も自宅の環境でもスプレッドシート(Excelではない笑)に記入しつつ運用してました。
で、業務で最近NetBoxというアドレス管理などを行うIPAMツールを使うことが増えたので、勉強がてら自宅にも導入してみました。

github.com

netbox.readthedocs.io

github.com

NetBoxって何

めちゃくちゃ雑に言うと「IPアドレス管理台帳のwebアプリ版」と解釈してます。
詳しくは公式サイトと、IPAM・DCIMなどで検索すると詳しい情報が見つかると思います。
元々はDigitalOcean社のネットワークエンジニアリングチームが開発したもので、PythonDjangoで実装されています。

インストール

環境

以下の環境にデプロイ。

[zaki@manager ~]$ cat /etc/centos-release
CentOS Linux release 7.9.2009 (Core)
[zaki@manager ~]$ docker -v
Docker version 20.10.2, build 2291f61
[zaki@manager ~]$ docker-compose -v
docker-compose version 1.27.4, build 40524192

また、NetBoxのバージョンは v2.10.3 となっている。

NetBoxインストール

Dockerのコンテナイメージが公開されており、Quickstartに沿ってDocker Composeを使えばサクッとデプロイできる。動作も軽量。

[zaki@manager local]$ git clone -b release https://github.com/netbox-community/netbox-docker.git
Cloning into 'netbox-docker'...
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 2417 (delta 0), reused 0 (delta 0), pack-reused 2412
Receiving objects: 100% (2417/2417), 624.70 KiB | 588.00 KiB/s, done.
Resolving deltas: 100% (1371/1371), done.
[zaki@manager local]$ cd netbox-docker/

ここで、ドキュメントだと「ホストOSでは8000/TCPでListenする設定」になるが、8000は別に使ってるので28080/TCPに変更。(数に深い意味はない)

$ tee docker-compose.override.yml <<EOF
version: '3.4'
services:
  nginx:
    ports:
      - 28080:8080
EOF

イメージをpull

[zaki@manager netbox-docker]$ docker-compose pull
Pulling redis         ... done
Pulling postgres      ... done
Pulling netbox-worker ... done
Pulling redis-cache   ... done
Pulling netbox        ... done
Pulling nginx         ... done

デプロイ

[zaki@manager netbox-docker]$ docker-compose up -d
Creating network "netbox-docker_default" with the default driver
Creating volume "netbox-docker_netbox-static-files" with local driver
Creating volume "netbox-docker_netbox-nginx-config" with local driver
Creating volume "netbox-docker_netbox-media-files" with local driver
Creating volume "netbox-docker_netbox-postgres-data" with local driver
Creating volume "netbox-docker_netbox-redis-data" with local driver
Creating netbox-docker_redis-cache_1   ... done
Creating netbox-docker_redis_1       ... done
Creating netbox-docker_postgres_1    ... done
Creating netbox-docker_netbox-worker_1 ... done
Creating netbox-docker_netbox_1        ... done
Creating netbox-docker_nginx_1         ... done
[zaki@manager netbox-docker]$ docker-compose ps
            Name                           Command               State                            Ports                         
--------------------------------------------------------------------------------------------------------------------------------
netbox-docker_netbox-worker_1   python3 /opt/netbox/netbox ...   Up                                                             
netbox-docker_netbox_1          /opt/netbox/docker-entrypo ...   Up                                                             
netbox-docker_nginx_1           /docker-entrypoint.sh ngin ...   Up      80/tcp, 0.0.0.0:28080->8080/tcp,0.0.0.0:49153->8080/tcp
netbox-docker_postgres_1        docker-entrypoint.sh postgres    Up      5432/tcp                                               
netbox-docker_redis-cache_1     docker-entrypoint.sh sh -c ...   Up      6379/tcp                                               
netbox-docker_redis_1           docker-entrypoint.sh sh -c ...   Up      6379/tcp    

割とすぐUpにはなるが、初回は内部ではDBの初期データ構築が行われている。

[zaki@manager netbox-docker]$ docker-compose logs -f
Attaching to netbox-docker_nginx_1, netbox-docker_netbox_1, netbox-docker_netbox-worker_1, netbox-docker_redis_1, netbox-docker_postgres_1, netbox-docker_redis-cache_1
netbox_1         | 🧬 loaded config '/etc/netbox/config/configuration.py'
netbox_1         | 🧬 loaded config '/etc/netbox/config/configuration.py'
netbox_1         | 🧬 loaded config '/etc/netbox/config/extra.py'
netbox_1         | Operations to perform:
netbox_1         |   Apply all migrations: admin, auth, circuits, contenttypes, dcim, extras, ipam, secrets, sessions, taggit, tenancy, users, virtualization
netbox_1         | Running migrations:

:
:

ログにnetboxのコンテナでListeneing at ... と出ていればだいたい大丈夫。

netbox_1         | 🧬 loaded config '/etc/netbox/config/configuration.py'
netbox_1         | 🧬 loaded config '/etc/netbox/config/configuration.py'
netbox_1         | 🧬 loaded config '/etc/netbox/config/extra.py'
netbox_1         | 
netbox_1         | 961 static files copied to '/opt/netbox/netbox/static'.
netbox_1         | ✅ Initialisation is done.
netbox_1         | [2021-01-09 09:04:31 +0000] [1] [INFO] Starting gunicorn 20.0.4
netbox_1         | [2021-01-09 09:04:31 +0000] [1] [INFO] Listening at: http://0.0.0.0:8001 (1)
netbox_1         | [2021-01-09 09:04:31 +0000] [1] [INFO] Using worker: sync
netbox_1         | [2021-01-09 09:04:31 +0000] [20] [INFO] Booting worker with pid: 20
netbox_1         | [2021-01-09 09:04:31 +0000] [21] [INFO] Booting worker with pid: 21
netbox_1         | [2021-01-09 09:04:31 +0000] [22] [INFO] Booting worker with pid: 22

webブラウザでHTTPアクセスすればNetBoxの画面にアクセスできる。

f:id:zaki-hmkc:20210111161156p:plain

コンテナは以下のものが動いているが、起動してしまえば(初回の初期処理が終われば)負荷はあまりない。

  • Nginx
  • NetBox
  • NetBox Worker
  • Redis
  • PostgreSQL
  • Redis Cache

ログイン

画面右上の「Log in」から。 Docker Composeで構築した場合はユーザー名・パスワードともにadminとなっている。

管理方針 (仮)

軽くさわってみた感じだと、自宅の環境(NW機器が特に無く、Linux/Windows等のサーバー・クライアントPCばかり)の管理としてはNUCなどの物理機器の情報はDCIM -> Devicesに、VMVirtualization -> Virtual Machinesにホスト情報を登録すると良さそうだった。
(あくまで使い方の一つで、私はこうやって使ってみようというもの。使ってるともっと別の使い方が良いと思えば変更するかも。)

ただし、ホスト情報の登録には選択式のパラメタがいくつか必須で、そのパラメタの選択肢を予め作成しておく必要がある。

Devices

バイス情報を登録するには、以下の必須項目があるので、まずこれらを作成しておく。

  • Site
  • Device Role
  • Device Type
  • Manufactures (Device Typeの作成に必要)

あと、必須ではないが、Rackなんかは自宅ラック勢には設定すると楽しいかもしれない。
自宅ラック勢でなくても、マシンの棚があってサーバー・ルーター・モデム・外付けHDDなどの機器がそれぞれ何段目にあるかを登録したりもできるかも?(未確認)

あとTag設定も可能で、これは事前に使いたいタグ情報を作成しておけば、複数のタグを設定できる。

Sites

f:id:zaki-hmkc:20210111161709p:plain

[Organization -> Sites]から。
画面上部のメニューの+か、Sites画面の右上の+ Addから項目を作成する。

名前の通り場所を登録する。例えば「自宅」とか。

Slugには、URLにも使われるユニークな識別子を入力する。
APIなどでも使用するので、英数字とハイフン程度にしておくのが無難。(たぶん)
名称にスペースを含むとハイフンになるなど、ASCII文字であれば自動で反映される。

項目
Site / Name 自宅
Site / Slug home
Site / Status Active

こんな感じ。

f:id:zaki-hmkc:20210111161908p:plain

Device Roles

名前の通り、デバイス(今回はホスト)の役割を定義。
サイトと同じ要領で、「Devices」の「Device Roles」から、「+ Add」押下で作成画面になる。

本来はNW機器の役割の登録に使用するっぽいが、自宅の環境はISPルーターと家電店で買ってきたスイッチくらいしかなく、メインの管理対象はサーバーとクライアントPCのため、あまり細かく分けずに「サーバー」「クライアント」「k8sノード」「ネットワーク」「テンポラリ」みたいに設定してみる。
なお、デバイスに割り当てられるロールは一つのみ。

f:id:zaki-hmkc:20210111162112p:plain

VM Roleのチェックについては、DevicesだけでなくVirtual MachinesのRoleの設定で割り当て対象とするかどうか、というもの。
(DevicesではRoleの設定は必須だが、Virtual MachinesではRoleの設定は任意)

Manufacturers

Devicesでデバイス登録するのに必須なのはDevice Typesだが、Device Typesの項目を作るのに必須なのがManufacturersになっている。
バイス登録時にDevice Typesを選択すると、自動的にManufacturersが決定されるようになるため、親子関係に当たる。

なので、初めはmanufacturesbare metal,VMを設定してDevice TypesLInux / Windows / ...なんて設定してみたけど構造的にイケてなかったのとVM登録用のVirtual Machinesという項目もあったので、結局名前の通り「Manufacturersには物理デバイス・PCのメーカー」と「Device Typesにはデバイス・PCのモデル名」という使い方にしてみた。

f:id:zaki-hmkc:20210111162158p:plain

Device Types

Manufacturersに機器メーカーを登録したら、Device Typesに機器のモデル名を登録。
元々データセンターのラックマウントを想定した設定内容のためHeight (U)という項目はあるが、一般のご家庭なので使用しないが必須項目のが目デフォルトの1で。

f:id:zaki-hmkc:20210111162235p:plain

Deviceの登録

  • Site
  • Device Role
  • Device Type

以上の必須項目の準備ができたのでデバイスを登録する。
名前はホスト名。
前述の通り、Manufacturerは空欄のままでも、「Create」したときに選択していたDevice typeから自動で設定される。

Virtualizationのところで選択できるCluster GroupあるいはClusterは、Virtual Machinesでは必須の項目だが、Devicesでは任意。

f:id:zaki-hmkc:20210111162349p:plain

画面の通り、一覧ではTypeのフィールドが「Manufacturer + Device Type」という表示になっている。

インタフェースとIPアドレス設定 (デバイスから作成)

Deviceの登録をしただけだと、IPアドレスの設定箇所はない。
IPアドレスを設定するには、対象デバイスの管理画面の+ Add ComponentsからInterfacesIPアドレスを設定するインタフェースを追加する必要があるので、まずインタフェースを作成する。

f:id:zaki-hmkc:20210111162712p:plain

インタフェースの作成はインタフェース名とTypeを選ぶ。今どきの物理PCなら1000BASE-Tとかそんな感じのはず。

f:id:zaki-hmkc:20210111162942p:plain

インタフェースが作成できたら、IPアドレスを設定できる。
IPアドレスは予めIPAM -> IP Addressesで作成性済みのものを割り当てもできるが、インタフェースの画面から新規に作成することもできる。

f:id:zaki-hmkc:20210111163112p:plain

項目
Address そのインタフェースのIPアドレス/マスク値
Status 使ってるのでActive
Interface Assignment 今はデバイスの設定なのでDeviceでホスト名とインタフェース名を指定
primary IP for the device/VM メインのNICであればチェック

一覧で表示されるのはPrimary IPのため、primary IP for the device/VMのチェックが入ってないアドレスはデバイス一覧では確認できない。

f:id:zaki-hmkc:20210111163218p:plain

ちなみに重複したIPアドレスは登録は可能だが、IPAMの画面から見るとDuplicate IP Addressという表示になる。

f:id:zaki-hmkc:20210111163252p:plain

Virtual Machines

バイスの登録ができたので、今度はVMを登録してみる。
VMの登録に必要な必須パラメタは以下の通りで、デバイスに比べると項目は少ない。

  • Cluster
  • Cluster Type (Clusterの作成に必要)

ただし、Clusterの作成にはCluster Typeを作成しておく必要がある。

Cluster Types

Cluster TypeにはVMのプラットフォームの名称を入れておくとよさげ。
ドキュメントではVMware vSphereなど。
うちはvSphereは使ってないのでESXiとかHyper-Vとかのハイパーバイザーを登録しておいてみる。

f:id:zaki-hmkc:20210111163732p:plain

Cluster TypesClusterの親情報となり、デバイスにおけるManufacturersDevice Typesの関係に近い。

Clusters

VMの論理グループを作成できる。
名称とCluster Typeを選択するだけだが、デバイス情報に登録する際に必須だったSite情報もここに設定できる。

f:id:zaki-hmkc:20210111163826p:plain

自分の環境だとイマイチ使い道が(今のところ)ないので、(とりあえず)ESXiなどのポートグループ名などVMが所属するネットワークを入れておく。

1/28追記: スペースを含んでいてもNetBox上には登録できるが、NetBoxにAPIアクセスするツール側で処理できない場合があるので、アンダーバーを使うなどした方が無難。

VMの登録

必須項目であるClusterの準備ができたのでVMを登録する。
前述の通り、デバイスの登録時に使用したRoleも任意で指定できる。

f:id:zaki-hmkc:20210111164032p:plain

あとオプションだが、VMのリソース(CPUやメモリ、ストレージ)も設定できる。
(ただしストレージは一つのみなので、「ディスク2本載せている」みたいなのは現状では表現できなさそう)

f:id:zaki-hmkc:20210111164136p:plain

ちなみにこのmanagerはNetBoxを動かしてるホスト。

インタフェースとIPアドレス設定 (VMから作成)

VMを登録しただけだとまだIPアドレスは未設定・設定できないので、デバイスと同様に+ Add Interfacesからインタフェースを追加して、IPアドレスを設定する。

f:id:zaki-hmkc:20210111164329p:plain

インタフェースを追加したら、デバイスのときと同様に+ボタン押下で新しくIPアドレスを作成・設定できる。
Interface Assignmentについてはデバイスのとき異なり、Virtual Machineタブの方を設定する。

f:id:zaki-hmkc:20210111164802p:plain

これでVirtual Machinesの画面に戻ると、作成したVMの一覧が表示される。

f:id:zaki-hmkc:20210111164926p:plain

ちなみに、インタフェースの追加時にはens[192,225]のように入力すれば複数インタフェースをまとめて作成できる。

f:id:zaki-hmkc:20210111165011p:plain

IPアドレス作成 (IP Addressesから)

作成したIPアドレスの一覧はIPAM -> IP Addressesから確認できる。
また、この画面の+ Addから作成も可能。

f:id:zaki-hmkc:20210111165105p:plain

複数まとめて作成

IPアドレスの画面から作成する場合は、Bulk Createのタブから複数アドレスをまとめて作成もできる。

f:id:zaki-hmkc:20210111165314p:plain

DHCPのアドレスレンジ

アドレスレンジの対象を前述の複数アドレス登録を指定しつつ、StatusからActiveでなくDHCPを選ぶ。
「〇〇というPCやスマホは今このアドレスを使っている」というリース状態をNetBoxで管理するのは難しそうな気がする(DHCPサーバーのリース状況をNetBoxが監視できる?)けど、「少なくともこのアドレスはDHCPの管理下なのでstaticな設定で使っちゃダメ」というのは見てわかるようになる。

f:id:zaki-hmkc:20210111165359p:plain

もしかするとDHCPサーバー側がNetBoxからリースするアドレスレンジを取得する、というのはできたりするかも?(詳しくチェックはしてないけど、netbox dnsmasqとかisc-dhcp-server netboxググるとそれっぽい情報がヒットする)

Kubernetes + MetalLBで使用するLoadBalancer Service用のIPアドレスもこの辺で予約しておくと良さそう。

IPアドレス使用状況

f:id:zaki-hmkc:20210111165530p:plain

IPAM -> IP Addressesから作成済みIPアドレスの一覧は確認できるが、「誰が使ってるのか」「未定義(未作成)のIPアドレスがどれくらいあるのか」はパッとわからなかったり分かりづらい。
これはIPAM -> Prefixesを使うと俯瞰しやすい一覧を作れる。

+ AddでPrefixの作成画面から以下のように入力。

項目
Prefix 一覧対象にするネットワークアドレス(192.168.0.0/24など)
Status Active
Is a pool チェック無し
Site 必要に応じて作成済みSite

※ Is a poolはチェックすると、ネットワークアドレスとブロードキャストアドレスに相当するアドレス(上の例だと192.168.0.0192.168.0.255)も"Available"扱いになるので扱いには注意。「ネットワーク全体」をNetBoxで管理する場合はチェックを外し、ネットワーク管理者は別に存在して「あなた(あなたのチーム)はこのアドレスを自由に使ってどうぞ」と言われて頭から尻尾まで全部ホストに割り当てられるアドレスを管理するのであればチェックすれば良いと思う。

f:id:zaki-hmkc:20210111165636p:plain

作成すると192.168.0.0/24ネットワークの定義画面になり、この画面の「IP Addresses」を開くと、

  • 未使用のアドレスとその数
  • 使用済みアドレスの使用インタフェースとその親リソース(VMやデバイス)

が簡単に一覧から確認できる。 ネットワークマスクを調整し、サブネットワークアドレスについてもここで一覧にできる。

f:id:zaki-hmkc:20210111165727p:plain


APIなど (リンク)

tekunabe.hatenablog.jp

qiita.com

qiita.com

REST API使って参照や追加したり、Ansibleの入力(ダイナミックインベントリ)に使ったりもできるけど一旦ここまで。(→ ざっくり試してみた)
(画面キャプチャや説明文書きつつ手動操作してたというより先日のPC設定崩壊の復旧で力尽きた)

NetBoxを手動で使うとIPアドレス管理台帳.xlsxからツールが変わっただけ(で登録についてはむしろ手間がちょっと多い)だけど、NetBoxのAPIと連携することで、VM作成時に情報の登録を自動化に組み込んだり、メンテナンスや監視の対象ホストをNetBoxから動的に取得したりすることが可能になる、はず。つまり、管理の自動化と相性が良い

Prefixの画面だけでもExcelの管理台帳から入れ替えできる情報量になっててよさそう。


まだまだ使い始めたばかりなので「NetBox使うのに〇〇機能を使わないのはおかしい」とか「その機能はそうじゃなくてこう使うんだよ」とか色々あるかもしれないけど、「とりあえず入れてみて手を動かしてみた」をやらないと前に進まないので試してみた記録。

ちなみに自宅で運用してたIPアドレス管理台帳(Googleスプレッドシート)はこんな感じ。(一部)
だいぶカオスってる。

f:id:zaki-hmkc:20210111213023p:plain


1/28追記: 本文中にも書いているが、スペースを含んでいてもNetBox上には登録できるが、NetBoxにAPIアクセスするツール側で処理できない場合があるので、アンダーバーを使うなどした方が無難。

[Ansible] ターゲットホストのIPアドレスの参照とデフォルトゲートウェイの設定についてのメモ

Ansibleの実行の際に、設定ファイルを作ったりしたい場合に使えると便利なターゲットホストのIPアドレスの参照方法について。
内容は「動かしてみてその内容をまとめたもの」なので、ドキュメントやソースコードを追ったりまでは確認できていないです。

playbook

- hosts: gateway-test
  gather_facts: true
  gather_subset:
    - network

  tasks:
  - name: print ansible_host
    debug:
      msg:
        - "{{ ansible_host }}"
        - "{{ inventory_hostname }}"
        - "{{ ansible_facts.default_ipv4 }}"  # <- gather_facts: false だと参照不可

インベントリファイルは以下の通り

inventory_hostnameは、インベントリファイルに記載しているホスト名がセットされる。
ansible_hostは、インベントリファイルのホスト名に対してansible_host=でアドレスを明記しているとその値がセットされる。指定が無い場合はinventory_hostnameと同じホスト名がセットされる。

ansible_facts.default_ipv4gather_factsが有効の場合にセットされる。オフの場合は(ansible_facts自体はあるがdefault_ipv4が)undefinedとなる。

なお、factsの取得はネットワーク関連のみで良い場合は、

  gather_subset:
    - network

を追加することで対象情報を絞ることができる。
(詳しくは、Red Hat 齊藤秀喜さんの「はじめてのAnsibleトラブルシューティング」スライド46ページ参照)

www.slideshare.net


以下は、default_ipv4には何がセットされるのかについてのメモ。

ターゲットホストのOS設定

アドレス設定

処理対象のターゲットホストはCentOS 7でNICが2つ。
設定は以下の通り。

[zaki@vm-example ~]$ nmcli c s "System ens192" | grep ipv4
ipv4.method:                            manual
ipv4.dns:                               192.168.0.19
ipv4.dns-search:                        --
ipv4.dns-options:                       ""
ipv4.dns-priority:                      0
ipv4.addresses:                         192.168.0.37/24
ipv4.gateway:                           192.168.0.1
ipv4.routes:                            --
ipv4.route-metric:                      -1
ipv4.route-table:                       0 (unspec)
ipv4.routing-rules:                     --
ipv4.ignore-auto-routes:                いいえ
ipv4.ignore-auto-dns:                   いいえ
ipv4.dhcp-client-id:                    --
ipv4.dhcp-timeout:                      0 (default)
ipv4.dhcp-send-hostname:                はい
ipv4.dhcp-hostname:                     --
ipv4.dhcp-fqdn:                         --
ipv4.never-default:                     いいえ
ipv4.may-fail:                          はい
ipv4.dad-timeout:                       -1 (default)
[zaki@vm-example ~]$ nmcli c s "System ens224" | grep ipv4
ipv4.method:                            manual
ipv4.dns:                               172.16.1.0
ipv4.dns-search:                        --
ipv4.dns-options:                       ""
ipv4.dns-priority:                      0
ipv4.addresses:                         172.16.0.37/23
ipv4.gateway:                           --
ipv4.routes:                            --
ipv4.route-metric:                      -1
ipv4.route-table:                       0 (unspec)
ipv4.routing-rules:                     --
ipv4.ignore-auto-routes:                いいえ
ipv4.ignore-auto-dns:                   いいえ
ipv4.dhcp-client-id:                    --
ipv4.dhcp-timeout:                      0 (default)
ipv4.dhcp-send-hostname:                はい
ipv4.dhcp-hostname:                     --
ipv4.dhcp-fqdn:                         --
ipv4.never-default:                     いいえ
ipv4.may-fail:                          はい
ipv4.dad-timeout:                       -1 (default)

default gatewayが設定されているのはens192のみ。

ens192のみgateway設定

前述の設定の状態でansible_facts.default_ipv4の値を参照してみる。

[zaki@vm-example ~]$ nmcli c s "System ens192" | grep ipv4.gateway
ipv4.gateway:                           192.168.0.1
[zaki@vm-example ~]$ nmcli c s "System ens224" | grep ipv4.gateway
ipv4.gateway:                           --
TASK [print ansible_host] ****************************************************************************
ok: [192.168.0.37] => 
  msg:
  - 192.168.0.37
  - 192.168.0.37
  - address: 192.168.0.37
    alias: ens192
    broadcast: 192.168.0.255
    gateway: 192.168.0.1
    interface: ens192
    macaddress: 00:50:56:01:23:45
    mtu: 1500
    netmask: 255.255.255.0
    network: 192.168.0.0
    type: ether

デフォルトゲートウェイが設定されているens192のネットワーク情報が取れている。

ens226のみgateway設定

デフォルトゲートウェイの設定をens192からens224に設定し直す。

[zaki@vm-example ~]$ sudo nmcli c m "System ens192" ipv4.gateway ""
[zaki@vm-example ~]$ sudo nmcli c m "System ens224" ipv4.gateway 172.16.1.0
[zaki@vm-example ~]$ nmcli c s "System ens192" | grep ipv4.gateway
ipv4.gateway:                           --
[zaki@vm-example ~]$ nmcli c s "System ens224" | grep ipv4.gateway
ipv4.gateway:                           172.16.1.0
[zaki@vm-example ~]$ sudo systemctl restart network
TASK [print ansible_host] ****************************************************************************
ok: [192.168.0.37] => 
  msg:
  - 192.168.0.37
  - 192.168.0.37
  - address: 172.16.0.37
    alias: ens224
    broadcast: 172.16.1.255
    gateway: 172.16.1.0
    interface: ens224
    macaddress: 00:0c:29:f0:53:1f
    mtu: 1500
    netmask: 255.255.254.0
    network: 172.16.0.0
    type: ether

デフォルトゲートウェイ設定を削除したens192ではなく、新たに設定したens224のネットワーク情報が取れている。

gateway設定無し

デフォルトゲートウェイの設定を全て削除してみる。

[zaki@vm-example ~]$ sudo nmcli c m "System ens192" ipv4.gateway ""
[zaki@vm-example ~]$ sudo nmcli c m "System ens224" ipv4.gateway ""
[zaki@vm-example ~]$ nmcli c s "System ens224" | grep ipv4.gateway
ipv4.gateway:                           --
[zaki@vm-example ~]$ nmcli c s "System ens192" | grep ipv4.gateway
ipv4.gateway:                           --
[zaki@vm-example ~]$ sudo systemctl restart network
TASK [print ansible_host] ****************************************************************************
ok: [192.168.0.37] => 
  msg:
  - 192.168.0.37
  - 192.168.0.37
  - {}

値が入ってない。
(ansible_facts.default_ipv4という変数自体はあるが中身が空)

両方にgateway設定

そもそもこの設定はアリなのか?というのは置いておいて…

[zaki@vm-example ~]$ sudo nmcli c m "System ens192" ipv4.gateway "192.168.0.1"
[zaki@vm-example ~]$ sudo nmcli c m "System ens224" ipv4.gateway 172.16.1.0
[zaki@vm-example ~]$ nmcli c s "System ens192" | grep ipv4.gateway
ipv4.gateway:                           192.168.0.1
[zaki@vm-example ~]$ nmcli c s "System ens224" | grep ipv4.gateway
ipv4.gateway:                           172.16.1.0
[zaki@vm-example ~]$ sudo systemctl restart network
TASK [print ansible_host] ****************************************************************************
ok: [192.168.0.37] => 
  msg:
  - 192.168.0.37
  - 192.168.0.37
  - address: 192.168.0.37
    alias: ens192
    broadcast: 192.168.0.255
    gateway: 192.168.0.1
    interface: ens192
    macaddress: 00:50:56:01:23:45
    mtu: 1500
    netmask: 255.255.255.0
    network: 192.168.0.0
    type: ether

ens192の値が取れてる。1番目のNICかな?
(でも、そもそもNICに順番ってあったっけ)

この辺りの仕様について書かれたドキュメントはちょっと見つけられず。

インタフェースを指定して参照

setupなどを使って収集したfactsの値を見ていると、ansible_default_ipv4以外にansible_ens192ansible_ens224はあるので、これを参照すれば良い。
システムによってはインタフェース名はens192でなくeth0などになるかもしれない。
(手元の環境はVMware ESXi上のVM)

インタフェース名を指定

  tasks:
  - name: print ens224.ipv4
    debug:
      msg: "{{ ansible_facts.ens224.ipv4 }}"

ens224を直接指定(ハードコーディング)してPlaybookを実行すると、

ok: [192.168.0.37] => 
  msg:
    address: 172.16.0.37
    broadcast: 172.16.1.255
    netmask: 255.255.254.0
    network: 172.16.0.0

この通り取得できる。
このときデフォルトゲートウェイの設定に関係なくIPアドレス情報は取得できるが、デフォルトゲートウェイの設定は.ipv4の中には含まれない。

インタフェースリスト名も取得

インタフェースの一覧はansible_facts.interfacesで取得できる。

  - name: print interfaces
    debug:
      msg: "{{ ansible_facts.interfaces }}"

これで実行すると

ok: [192.168.0.37] => 
  msg:
  - lo
  - ens192
  - ens224

なのでこのリストからインタフェース名をピックアップし、それを使って ansible_fact[〇〇].ipv4とすれば動的に取得することもできる。

  - name: print ipv4 address (nic1)
    vars:
      nic: "{{ ansible_facts.interfaces[1] }}"
    debug:
      msg: "{{ ansible_facts[nic].ipv4 }}"

一度varsでインタフェース名リストの2番の項目(ens192)を変数nicにセットし、そのipv4アドレスを取得。

ok: [192.168.0.37] => 
  msg:
    address: 192.168.0.37
    broadcast: 192.168.0.255
    netmask: 255.255.255.0
    network: 192.168.0.0

このようにens192というインタフェース名をplaybook側で知っていなくても取得は可能。

VS CodeのRemote DevelopmentでSSHアクセスすると接続先の高負荷状態が続く問題と対応

年末くらいから、メインの作業用Linuxマシン(この1台のみ)にVS CodeのRemote DevelopmentでSSHアクセスすると一定時間高負荷状態が続いていたので、なんとかならないかと原因調査した結果。
なお、クライアント側のVS CodeはどのPCから接続しても、特定の接続先Linuxマシンのみで高負荷になっていたので、VS CodeをインストールしているPC側の問題ではなかった ないだろうと見当をつけて調査。

VS Code version: 1.52.1

VS CodeのRemote SSHでnodeプロセスが高負荷になる

Remote Development SSHでアクセスした時のtopの状態がこちら。
接続してすぐではなく、この状態が何分も続く。(なのでマシンのファンが騒がしい…)

top - 22:00:33 up 15 days,  1:25,  1 user,  load average: 3.01, 1.73, 1.45
Tasks: 382 total,   2 running, 376 sleeping,   0 stopped,   4 zombie
%Cpu(s): 23.7 us, 18.6 sy,  0.0 ni, 57.6 id,  0.0 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem :  7990060 total,  1515608 free,  4614492 used,  1859960 buff/cache
KiB Swap:  3670012 total,  3628028 free,    41984 used.  2947736 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
3171205 zaki      20   0 2935216   2.0g  13376 R 164.1 26.7   1:53.15 node
3170035 zaki      20   0 1066544  86940  16932 S   2.7  1.1   0:05.45 node
 413552 root      20   0  231440  32184  12556 S   0.7  0.4  24:36.47 Rib
   1085 root      20   0  676336  34308   2792 S   0.3  0.4   6:45.09 containerd
 166921 root      20   0  493944  21524   3284 S   0.3  0.3   8:58.71 AgentMonitor
 413183 root      20   0  500180  23996   1972 S   0.3  0.3  19:08.39 ProcMgr-worker
1202714 zaki      20   0 1362644  77680  17112 S   0.3  1.0   7:57.84 node
3171254 zaki      20   0  162380   2532   1576 R   0.3  0.0   0:00.14 top
      1 root      20   0  193892   6664   3848 S   0.0  0.1   3:03.00 systemd
      2 root      20   0       0      0      0 S   0.0  0.0   0:00.21 kthreadd
      4 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H
      6 root      20   0       0      0      0 S   0.0  0.0   0:05.28 ksoftirqd/0
      7 root      rt   0       0      0      0 S   0.0  0.0   0:07.45 migration/0
      8 root      20   0       0      0      0 S   0.0  0.0   0:00.00 rcu_bh
      9 root      20   0       0      0      0 S   0.0  0.0   9:38.45 rcu_sched
     10 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 lru-add-drain
     11 root      rt   0       0      0      0 S   0.0  0.0   0:03.74 watchdog/0
     12 root      rt   0       0      0      0 S   0.0  0.0   0:03.72 watchdog/1

こんな感じでload averageは余裕の1超でnodeプロセスが大量にCPUを消費している。
nodeプロセスはps auxf等で確認すれば、VS CodeのRemote Developmentのサーバー側プロセスのもののであることがパスから確認できる。(パスが$HOME/.vscode-server/bin以下になっている)
そしてLinuxホストをrebootしても状況変わらず。

大量のreportファイル

さらに、何度もこの状態に遭遇しているとホームディレクトリ以下にJSON形式の謎のレポートファイルが大量に生成されている。

[zaki@cloud-dev ~]$ ls report.*
report.20201219.104229.1068593.0.001.json  report.20201222.202242.102134.0.001.json
report.20201219.104436.1068978.0.001.json  report.20201222.202458.102602.0.001.json
report.20201219.104725.1069212.0.001.json  report.20201222.203137.103212.0.001.json
report.20201219.104938.1069512.0.001.json  report.20201222.203827.9151.0.001.json
report.20201219.105220.1069896.0.001.json  report.20201222.204039.9613.0.001.json
report.20201219.105508.1070240.0.001.json  report.20201222.204240.9826.0.001.json
report.20201219.105731.1070505.0.001.json  report.20201222.204457.10034.0.001.json
report.20201219.213527.1129837.0.001.json  report.20201222.204721.10252.0.001.json
:
:
[zaki@cloud-dev ~]$ ls report.* | wc -l
122

中身を見ると、

{
  "header": {
    "reportVersion": 1,
    "event": "Allocation failed - JavaScript heap out of memory",
    "trigger": "FatalError",
    "filename": "report.20201219.104229.1068593.0.001.json",
    "dumpEventTime": "2020-12-19T10:42:29Z",
    "dumpEventTimeStamp": "1608342149806",
    "processId": 1068593,

:
:

という感じで、ヒープメモリ不足のエラーが出力されている。(ちなみにOSの搭載メモリは8GB)
手元の環境だと、20分くらい高負荷の状態が継続し、挙句にnodeプロセスがクラッシュして終了する涙
(VS CodeのRemote Development自体は使用可能)

原因

いろいろ調べた結果、straceなんかで処理を眺めているとPythonのvenvディレクトリ内のファイルが大量に出力されており、どうやら「Remote Development SSHでアクセスするホストのファイルをVS Codeのファイルエクスプローラーでwatchするための情報収集」するために大量のCPU・メモリが消費されていた模様。

watchの除外設定

watch excludeで設定を検索するとでてくる「Files: Watcher Exclude」の設定に追加する。

特に指定しなくてもデフォルトで以下のファイルグロブパターンは除外となっている。

    "**/.git/objects/**": true,
    "**/.git/subtree-cache/**": true,
    "**/node_modules/**": true,
    "**/.hg/store/**": true

今回、私の場合は、(venv環境は基本的にvenvというディレクトリ名・たまにvenv/a10とかvenv.hogehogeとかの配下に作ってるので)**/venv*/**という除外設定を以下のようにfiles.watcherExcludeに追加。

    "files.watcherExclude": {
        "**/venv*/**": true
    },

この設定を入れた状態でRemote Development SSHでリモートアクセスすると、今までは20分近く高負荷が続いてクラッシュしていた処理が、SSHログイン後約10はCPU負荷が高まるがすぐにIDLEに落ち着くようになった。素晴らしい。

なお、似たような名前で files.exclude という設定もあるが、こちらはファイルエクスプローラー上の可視設定になっており、watch対象にするかどうかには影響しない。
(こちらに除外設定を追加してもファイルエクスプローラーから見えなくなるだけでwatch処理対象のままで負荷は高まる)

2021.08.24追記
tox使った環境だとvenvとかがパスに入らずに仮想環境が作られるようになってたので、追加して現状こんな感じ。

    "files.watcherExclude": {
        "**/venv*/**": true,
        "**/env*/**": true,
        "**/.tox/**": true
    },

(余談) なぜこんなにvenvがあるのか

まっさらなAnsible環境をわりとカジュアルにポコポコ作ることが多く、python3 -m venv hogeして、そこでpip install ansibleしていたが、これがAnsible 2.10だと(VS CodeのRemote SSHではwatchの必要もない)3万以上のファイルが作成されて、これが標準設定のRemote SSHボトルネックになっていた笑


調子戻ったなぁ。。
(というか原因究明しなかったらこの新しいVMでも同じ目にあってたな)

まとめ

というわけで、同じような症状に悩んでる方は、対象ホスト上に何らかの理由で(十万単位レベル以上の)大量のファイルを作成したりしていないか確認してみましょう。


「この大規模なワークスペースでのファイルの変更をウォッチできません。この問題を解決するには、手順のリンクに従ってください。」

についてはこちら。

code.visualstudio.com

カーネルパラメタを設定してファイル数を増やす。
/etc/sysctl.d/99-sysctl.confファイルに以下を追加。

# for vscode
fs.inotify.max_user_watches=524288

追加したらrootでsysctl -pを実行して設定反映する。


なんか盛大にディスられてるのを見つけたけど、CPU使用率が一瞬でもスパイクするのを許せない方かな…?
補足するまでもないとは思うけど、このCPU使用率は通常の利用範囲です。

[Ansible] YAML/JSON書式の文字列を構造化データとして扱う

文字列として保持しているYAMLJSON形式のデータを構造化データとして扱うにはフィルタで変換するのが簡単です。
PowerShellConvertFrom-Jsonとかと同じやつです。

docs.ansible.com

YAML形式の文字列をオブジェクトに変換

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: sample-http
  name: sample-http
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sample-http
  template:
    metadata:
      labels:
        app: sample-http
    spec:
      containers:
      - image: httpd
        name: httpd

こんな感じのよく見かけるYAML形式のデータがありこれをstring型(plain/text)で変数に持っている場合に、構造化データとして処理するにはfrom_yamlフィルタを使うと簡単に処理できます。

以下はcatで読み込んで__resultにセットしたYAML形式の文字列データをfrom_yamlフィルタでdata変数にセット、あとは普通にdata変数を構造化データとして扱って辞書型やリスト参照しています。

- hosts: localhost
  gather_facts: no

  tasks:
  - name: get yaml file
    command: cat yamlfile.yml
    register: __result

  - name: get yaml value from text
    vars:
      data: "{{ __result.stdout | from_yaml }}"
    debug:
      msg: '{{ data.spec.template.spec.containers[0].image }}'

このPlaybookでansible-playbookを実行すると以下の通り。

TASK [get yaml value from text] ***********************************************
ok: [localhost] => 
  msg: httpd

JSON形式の文字列をオブジェクトに変換

JSON形式の場合も同様にfrom_jsonを使えば同じように処理できます。

{
    "apiVersion": "apps/v1",
    "kind": "Deployment",
    "metadata": {
        "labels": {
            "app": "sample-http"
        },
        "name": "sample-http",
        "namespace": "default"
    },
    "spec": {
        "replicas": 2,
        "selector": {
            "matchLabels": {
                "app": "sample-http"
            }
        },
        "template": {
            "metadata": {
                "labels": {
                    "app": "sample-http"
                }
            },
            "spec": {
                "containers": [
                    {
                        "image": "httpd",
                        "name": "httpd"
                    }
                ]
            }
        }
    }
}

このJSON(内容は前述のYAMLと同じ)に対して

  - name: get json file
    #command: curl https://ifconfig.io/all.json
    command: cat jsonfile.json
    register: __result

  - name: get json value from text
    vars:
      data: "{{ __result.stdout | from_json }}"
    debug:
      msg: "{{ data.spec.template.spec.containers[0].image }}"

この内容のPlaybookを実行すると以下の通り。

TASK [get json value from text] ***********************************************
ok: [localhost] => 
  msg: httpd

フィルターの結果の辞書やリストから直接値を取り出す

{{ result | from_json['spec'] }}とかは出来ないので、指定要素を取り出すフィルタが無いか探したけど処理が単純すぎるのか逆に見当たらず。。
結局カッコ使ってこんな感じに。。うまいやり方は他にあるのかな。。

    debug:
      msg: "{{ (__result.stdout | from_json).spec.template.spec.containers[0].image }}"

追記: attr()使えないかと思ったけどダメみたい。現状カッコしかないかなー


まとめ

catで読み込んだりしただけだと、結果のJSONYAML形式のデータは、書式はYAMLですが単なる複数行文字列データなので__result.stdout.data.spec ...のように構造化データとして処理は出来ません。
from_yamlフィルタなどを使うことで構造化データになるので、特定要素へのアクセスが簡単に処理できるようになります。
また、構造化データであればjson_query()のようなフィルタを使った処理も可能になります。

確認した環境は以下の通り。

$ ansible --version
ansible 2.10.3
  config file = /home/zaki/src/ansible-sample/yaml-pickup/ansible.cfg
  configured module search path = ['/home/zaki/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/zaki/src/esxi-ansible/venv/2.10/lib64/python3.6/site-packages/ansible
  executable location = /home/zaki/src/esxi-ansible/venv/2.10/bin/ansible
  python version = 3.6.8 (default, Nov 16 2020, 16:55:22) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]

[Kubernetes / Ansible] containerdをCRIに指定してクラスター構築するAnsible Playbook

明けましておめでとうございます。

年末に話題になった「KubernetesのコンテナランタイムでDocker(Dockershim)がv1.20で非推奨になる」にともない、CRIに「containerd」を指定したkubeadmを使ったクラスター新規構築を(お正月に)試してみたので簡単にまとめ。

thinkit.co.jp

ドキュメント

kubernetes.io

日本語訳:

kubernetes.io

手順の概要

既存の手順の中の「Dockerをインストールする」という箇所を、まるごと「containerdをインストールする」に入れ替えるだけなので、難しいことは特にないです。(Dockerが入っておらずcontainerdが入っていれば、containerdは自動で検出されるので、CRIの外側の手順は変更なし)

  1. カーネルモジュールのロード
    • overlay
    • br_netfilter
  2. カーネルパラメタの設定
  3. OS毎のcontainerdインストールと設定
    • CentOS/RHELの場合はDocker CEのリポジトリを追加してcontainerdyumでインストール
    • containerdの設定ファイルはsudo containerd config defaultで生成
    • containerdサービスのスタート

という流れ。

Ansible化

手順をそのままAnsibleのRoleにするとこんな感じ。
(昨年作った「kubeadmを使ったKubernetes構築するAnsible Playbook」にこれを組み込んでみた。)

github.com

のちにcontainerdサービス開始はhandlersで実装
また、/etc/sysctl.d/99-kubernetes-cri.confファイルはsysctlモジュールを使って生成できるので、このファイルをcopyモジュールで作成する処理は不要

構築時間

Kubernetesに必要な部分のみのラインタイムというだけあって、Dockerを使った構築に比べると1分近く構築時間が短くなりました。

PLAY RECAP ********************************************************************
master                     : ok=43   changed=37   unreachable=0    failed=0    skipped=12   rescued=0    ignored=1   
worker0                    : ok=26   changed=23   unreachable=0    failed=0    skipped=9    rescued=0    ignored=1   
worker1                    : ok=26   changed=23   unreachable=0    failed=0    skipped=9    rescued=0    ignored=1   


real    3m54.430s
user    0m23.420s
sys     0m8.903s
[zaki@cloud-dev initialize-kubeadm-ansible (fix/#41_typo_control_plane)]$ ssh master
Last login: Mon Jan  4 07:37:45 2021 from cloud-dev.okd4.naru.jp-z.jp
[zaki@master ~]$ kubectl get node -o wide
NAME      STATUS   ROLES                  AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION           CONTAINER-RUNTIME
master    Ready    control-plane,master   94s   v1.20.1   192.168.0.131   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   containerd://1.4.3
worker0   Ready    <none>                 37s   v1.20.1   192.168.0.135   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   containerd://1.4.3
worker1   Ready    <none>                 37s   v1.20.1   192.168.0.136   <none>        CentOS Linux 7 (Core)   3.10.0-1062.el7.x86_64   containerd://1.4.3

OS(CentOS 7)インストール直後の状態からの構築で全部完了するのに(Dockerの場合と比較すると1分近く速くなって)今のところ最速で4分弱。(new record!)
使用しているCRIは、kubectl get node -o wideで確認できます。

環境

(参考) 構築済みKubernetesのラインタイム変更

こちらについてはまだ試してないのでそのうち。。

repl.info

cyberagent.ai


そういえば今年1件目のブログでした。
今年もよろしくお願いします。

2020年の振り返り

2020年おつかれさまでした。

元旦のブログ振り返り

2019年は主に自動化とOpenShiftベースのコンテナ技術について、特に仕事終わった後のテック系コミュニティに積極的に参加(聴講側)してきましたが、今年もその方面で頑張りたいです。
特に仕事ではOpenShiftはクラスタのインストールばかりなので、クラスタ上で動作するアプリとか(業務で無理なら趣味の範囲ででも)もっとやりたいですね。とりあえずこれのQuarkus対応はタスクに積みっぱなし。

あとはLT登壇の実績解除もそろそろ果たしたいところ…(usansibleあたりかな?)

1月1日のブログ

「OpenShift 物を動かす」については業務でその辺りの担当から外れてしまったので結局かなわず…(そもそもIaaSのチームだったので上物は触る予定も無かった)
個人環境でBuildConfigやS2Iを使ったビルド&デプロイをある程度は試せたのでよかったですが、Tektonなどのパイプラインビルドまでは手が回らなかった。
そしてQuarkusについては相変わらず手付かず。。

CI/CD周りとかやりたいけど未だにできてないので来年こそ…!

転職

今年一番大きな出来事としては、やっぱり10月に転職したこと。

昨年末くらいから真面目に転職を考え始め、夏くらいに「この人たちのいる会社で一緒に働きたい!」という想いが叶って無事内定を頂けて本当に感謝です。
履歴書の志望動機はその人へのファンレターみたいな内容を書いた…ような気がしてたけど、今読み返すとそうでもないかな?(笑)
趣味でさわっていたAnsibleがついに仕事に。

ちなみに、職務経歴書はMSWordで4ページ作成。
職務経歴書はマスタデータを手元にもっておいて、転職の有無に関係なく振り返りとして定期的に職務経歴の棚卸すると良いですね。
実際に使用する場合はマスタデータの中からいくつかピックアップすればいいとして、とりあえず仕事1個完了するようなタイミングで追記しておくと良いかもしれない。
あと、履歴書用の写真は一眼と三又一脚使って部屋で自撮りしました。

転職先は、、、どこかで喋ってたり載ってたりしています。

ネットワーク

もっと頑張りましょう。。。
でも入社前にこれさわってたのはかなり良かったです。

zaki-hmkc.hatenablog.com

HTTPやSMTPtelnetで喋ったりTCPソケットプログラミングとかのレイヤーなら分かるんだけど、ルーティングプロトコルとかネットワークOSのCLI操作方面になるとまだまだ。。

Kubernetes

何だかんだでずっとOpenShiftオンリーだったので、Kubernetesをがっつり(いうほどか?)さわったのは実は今年から。
特にkubeadmを使ったデプロイAnsibleで自動化したり、公式ドキュメントの日本語翻訳に関われたのはいい経験になりました。

github.com

本当はもっとKubernetes自体のコードを追ったりもしたくてGo言語とかやりたかったけど、なかなか手が回らず…未だに雰囲気でコードリーディングしてます。
Kubernetes周りのエコシステムもまだまだ「使ってみた」が足りてない感。
そしてまだOperatorに入門すら出来ていない…

OpenShiftは仕事でさわる機会がだいぶアヤシクなってしまいましたが、OKDの構築は面白かったしCRCも使って何かやりたい。

zaki-hmkc.hatenablog.com

ESXiのVMへkubeadm使ったオンプレKubernetes構築は15分かからずに完了するようになったけど、まだまだ自動化の余地があるので改良したい。

振り返るとやっぱりクラスタデプロイの先があまりできてないなぁ。

コミュニティイベント

登壇

「今年はLT登壇してみたい」となんとなく目標設定してましたが、これは無事達成できました。

コロナ禍直前の2月にAnsibleコミュニティで1回 (めちゃくちゃ緊張して喋るのが困難になるレベルで呼吸が乱れましたがw)
その後オンラインイベントに移行して6月に2回、12月に1回の計4回、登壇枠で参加することができました。

ドキュメント翻訳のコントリビュートでKubernetes(かKubernetes Novice)参加してみたかったのですがちょっと機を逸してしまいました。
2021年も同じくらいの回数はできたらいいな。

Ansible

1月にメンター候補で参加して2月からメンターとしてもくもく会に参加するようになりました。
いろいろと問い合わせ対応してるのは今までの経験も活きてるし、これからの業務にも活かせそうで参加してよかったです。

毎回2,3件を目標に回答するようにしてるけど、対応できなさそうな内容の場合も、状況整理のために実行したコマンドや画面キャプチャを貼ってもらって他の人が回答できそうな状態にするお手伝いはできるので、これからも継続したいです。

アドベントカレンダー

2019年は最終的に7件(全てQiita)書いてかなりしんどかったので「2020年は余裕作るために数へらそー」とか思って4件しかエントリーしなかったんですが、なんだかんだで最終的に8件(Qiita 6件とAdventar 2件)に増えてしまって不思議です。。

1件参加を複数やるのも良いんだけど、「〇〇に入門してみた全部オレ」みたいに1つのテーマを25分割してまとめるのも面白いかなぁと思ったり。
イメージとしては「Python始めてみた全部オレ」で基本構文とかから入ってOOPやったり終盤はDjangoとかコンテナで実行とか。

テレワークと自炊

テックじゃない話も…ここから本題?笑

こんな世の中だし自分も4月の上旬くらいからようやく在宅勤務できるようになりました。
で昼食。
最初の頃は近所の定食屋さんのランチテイクアウトとかいろいろと利用してましたが、以前から一度「昼休みに自炊をやってみたい」と思ってたのを思い出してチャレンジ。(そもそもここ数年自炊全然しない人だったけど)

で、今でこそペペロンチーノだいぶまともに(?)作れるようになったけど、当時は「ゆで汁を混ぜる」とかかなり適当な分量で火も通り過ぎてすごく茶色になって全然ペペロンチーノっぽくならなかった。
まさに「推測するな、計測せよ」です。
というか設計書通りに作りましょう。

ちなみに今の私のペペロンチーノの作り方はこの動画がベース。

www.youtube.com

カルボナーラは以前もよく作ってたけど、生クリームかホイップ使っててコスパ微妙だったので何とかならないかなーとずっと思ってて、試しに牛乳・チーズ・ヨーグルトで作ったら全然いけたのでスタメン変更しました。(のちにマヨネーズも追加)

ペペロンチーノとカルボナーラはだいぶ作ったなぁ。

それから麺類が続くけど夏はやっぱりソーメン。
なんかほとんど毎日お昼ソーメンだった気が…
トッピングいろいろ試すのが楽しかったですね。

そしてカレーもたくさん作りました。

今年一番活用した本です笑

作り置きできる「カレーだれ」が万能調味料的な感じで、このカレーだれをベースに作るレシピ本。
タイトル通り「煮込む」という行程のない手順になってるものが多いので、普通のカレーより手早く作れるのと、分量は2人前からだけど単に半分の量にすれば1食分だけ作るのも簡単なので、フライパンで炒める&蒸し焼きくらいで割と手軽に作れるのがとても良かった。
1人暮らしなんかだと特に料理しやすいと思う。

スパイスは「大さじ2」の分量のモノはスーパーのスパイスコーナーの小瓶に入ってるタイプだとあっという間に使い切ってしまう(何よりこの分量を計量スプーンに小瓶から取り出すのが死ぬほど面倒くさかった)ので、大量購入しました。

漫画とか

今年見た作品。
順番は見た順序なので優劣じゃないです。
偶然だけど全部ジャンプだなー

  1. 鬼滅の刃

    正月休み後半に暇になったので何気なく「そういえば『鬼滅の刃』って流行ってたみたいだしAmazonプライムビデオで見れるから見てみるかー」と見てみたのがきっかけ。
    家族愛もテーマの一つだと思ってるんだけど、ちょうど両親を関東に呼んでおもてなしした直後だったので余計に思うところがあった感じ。

    ちなみに1/3にアニメを見始めて、1/5の時点でコミックス全巻(当時19巻)まで見終わっていたらしい。

  2. SPY×FAMILY

    ツイッターのTLで何人かが新刊の話してて何となくチェックしてみたら好きな要素てんこ盛りの作品でした。
    基本的に裏稼業モノ・特殊能力モノ(あと、学校が舞台だったりするとなお良し)は大好物なので。

    ダミアン君のキャラクターがすごく良いw

  3. アクタージュ

    いろいろ話題になって既刊含めて出荷停止になると聞いて、でも作品は面白そうだったのでKindleで一気に全巻購入。
    妹が演劇やってたり舞台見に行ったりしたこともあったりした背景があったので、とても面白かった。
    舞台を全通した女の子がどうなったのか、続きが気になる作品なんで残念ですね。。

  4. 約束のネバーランド

    劇場版の鬼滅の刃見に行ったとき、上映前に映画の宣伝映像でこれが流れて「お、これおもろそう!」と思ってあとでチェックしたらドはまりしたというもの。
    アニメ化されてたのも何も知らなくて、ググって最初に見つけたのがコミックスだったのでひたすらKindleで買っては読んでを繰り返して、で読んでると巻末にアニメ情報もあり幸いAmazonプライムビデオで見ることができたので良かった。(11/3に読み始めて20巻読破からのアニメ半分まで進んだらしい)

    映画も面白かったし(そもそもこれが知ったきっかけだった)、2期のアニメも楽しみ。

    コミックスは毎週20巻周回してるけど、実は小説版はまだ2巻の途中。。
    そして英語版もKindleにあるのを見つけてしまい…

    というか、映画って年に1回行くか行かないかって感じなのに、こんな短期間に2回も行くのとか珍しい…

プロ野球観戦と遠征

仕方ないけど1試合も球場に行けなかった…
今年のオールスターは福岡だったのでみんなで遠征しようねー、ついでにどんどん亭に行ってお好み焼き食べようねーと楽しみにしてましたが、、いつか出来たらいいな。

あとコロナ禍前に群馬の友達の家まで遊びに行ったのが2020年の最初で最後の旅行だったのかな?
Ansibleタグを忘れてしまって #旅するAnsible できなかったのが本当に心残り(笑)

2021年やってみたいこと

  • Kubernetes関連でLT登壇 (ネタはまだ無い)
  • Ansible
  • 雑誌への寄稿もやってみたい、、、なんだけど2021年はまだ余裕ないかもしれない…(小声)
  • 料理のレパートリーをカレー・パスタ以外に増やそう笑

それでは良い2021をお迎えください。

SNMP Exporterを使ってArista EOSのSNMPの情報をPrometheusから参照する

SNMP Exporterをお試し。
環境は、SNMP ExporterもPrometheusもCentOS 7上で直接バイナリを実行している。(非K8s環境)
SNMPエージェントについてはDockerで動作するArista EOSを使用。

Prometheusバージョンは2.23を使用。

実行用バイナリの入手

github.com

2020.12.29時点で0.19.0(2020.08.31リリース)

CentOS上で動かすので「snmp_exporter-0.19.0.linux-amd64.tar.gz」をダウンロードする。

$ curl -LO https://github.com/prometheus/snmp_exporter/releases/download/v0.19.0/snmp_export
er-0.19.0.linux-amd64.tar.gz

Blackbox Exporterと同じように、実行バイナリと設定YAMLが含まれる。

[zaki@cloud-dev prometheus]$ tar xf snmp_exporter-0.19.0.linux-amd64.tar.gz
[zaki@cloud-dev prometheus]$ cd snmp_exporter-0.19.0.linux-amd64/
[zaki@cloud-dev snmp_exporter-0.19.0.linux-amd64]$
[zaki@cloud-dev snmp_exporter-0.19.0.linux-amd64]$ ls -lh
合計 15M
-rw-r--r--. 1 zaki zaki  12K  8月 31 21:15 LICENSE
-rw-r--r--. 1 zaki zaki   63  8月 31 21:15 NOTICE
-rw-r--r--. 1 zaki zaki 616K  8月 31 21:15 snmp.yml
-rwxr-xr-x. 1 zaki zaki  14M  8月 31 21:07 snmp_exporter

なお、設定に使用するsnmp.ymlは人手で作成せずにgeneratorを使って生成するとのこと。(未検証)

github.com

設定YAML、0.19.0だと616KBもあるけど、masterブランチのファイルは現時点で1.16MBもある。。

SNMP Exporterの実行

snmp_exporterを引数無しで実行すれば、実行時のカレントディレクトリにあるsnmp.ymlを設定ファイルとして自動で読み込んで起動し、9116/TCPでListenする。

[zaki@cloud-dev snmp_exporter-0.19.0.linux-amd64]$ ls 
LICENSE  NOTICE  snmp.yml  snmp_exporter
[zaki@cloud-dev snmp_exporter-0.19.0.linux-amd64]$ ./snmp_exporter 
level=info ts=2020-12-29T03:09:58.262Z caller=main.go:149 msg="Starting snmp_exporter" version="(version=0.19.0, branch=HEAD, revision=9dcbc02f59648b21fcf632de1b62a30df70f4649)"
level=info ts=2020-12-29T03:09:58.262Z caller=main.go:150 build_context="(go=go1.14.7, user=root@387afaad41d6, date=20200831-12:07:03)"
level=info ts=2020-12-29T03:09:58.305Z caller=main.go:243 msg="Listening on address" address=:9116

起動オプションは以下の通り。
--config.file=で設定ファイル名を指定も可能。

$ ./snmp_exporter --help
usage: snmp_exporter [<flags>]

Flags:
  -h, --help                    Show context-sensitive help (also try --help-long and --help-man).
      --snmp.wrap-large-counters  
                                Wrap 64-bit counters to avoid floating point rounding.
      --config.file="snmp.yml"  Path to configuration file.
      --web.listen-address=":9116"  
                                Address to listen on for web interface and telemetry.
      --dry-run                 Only verify configuration is valid and exit.
      --log.level=info          Only log messages with the given severity or above. One of: [debug, info, warn, error]
      --log.format=logfmt       Output format of log messages. One of: [logfmt, json]

snmp.ymlが無いとエラー

$ ls -a
.  ..  .snmp.yml  LICENSE  NOTICE  snmp_exporter
$ ./snmp_exporter
level=info ts=2020-12-29T02:47:13.169Z caller=main.go:149 msg="Starting snmp_exporter" version="(version=0.19.0, branch=HEAD, revision=9dcbc02f59648b21fcf632de1b62a30df70f4649)"
level=info ts=2020-12-29T02:47:13.169Z caller=main.go:150 build_context="(go=go1.14.7, user=root@387afaad41d6, date=20200831-12:07:03)"
level=error ts=2020-12-29T02:47:13.169Z caller=main.go:156 msg="Error parsing config file" err="open snmp.yml: no such file or directory"

webアクセス

SNMP Exporterが実行中の状態で9116/TCPへブラウザでアクセスするとこんな感じ。

f:id:zaki-hmkc:20201229142950p:plain

ここで、「Target」に前日作ったSNMPエージェントを有効にしたArista EOSのコンテナのアドレスを入力してみると、以下の通り。
(ウェルノウンポートでないので、ポート番号まで記載する。192.168.0.18:45161という感じ)

f:id:zaki-hmkc:20201229143053p:plain

# HELP ifNumber The number of network interfaces (regardless of their current state) present on this system. - 1.3.6.1.2.1.2.1
# TYPE ifNumber gauge
ifNumber 0
# HELP snmp_scrape_duration_seconds Total SNMP time scrape took (walk and processing).
# TYPE snmp_scrape_duration_seconds gauge
snmp_scrape_duration_seconds 0.007350904
# HELP snmp_scrape_pdus_returned PDUs returned from walk.
# TYPE snmp_scrape_pdus_returned gauge
snmp_scrape_pdus_returned 2
# HELP snmp_scrape_walk_duration_seconds Time SNMP walk/bulkwalk took.
# TYPE snmp_scrape_walk_duration_seconds gauge
snmp_scrape_walk_duration_seconds 0.007298412
# HELP sysUpTime The time (in hundredths of a second) since the network management portion of the system was last re-initialized. - 1.3.6.1.2.1.1.3
# TYPE sysUpTime gauge
sysUpTime 5.545402e+06

sysUpTimeの値である「5.545402e+06」についてはintにすると「5545402」。
これは参照時のArista EOSコンテナのUp Timeが15 hour ago(docker psで確認)なので値はだいたい一致している。

リロードすれば値は増加する。

arista_sw

デフォルトのif_mibでなく、対象機器に合わせてarista_swを指定してみると以下の通り。
スクロールバーからも分かる通り、様々な値を参照できている。

f:id:zaki-hmkc:20201229143112p:plain

※ この辺かなりわかってなくて、snmp.ymlの定義済み設定からそれっぽい項目をピックアップした。Ciscoであればcisco_wlcというものがある。

[zaki@cloud-dev snmp_exporter-0.19.0.linux-amd64]$ grep -n ^[a-z] snmp.yml 
2:apcups:
2324:arista_sw:
3560:cisco_wlc:
4660:ddwrt:
6084:if_mib:
7335:infrapower_pdu:
7419:keepalived:
8904:nec_ix:
11173:paloalto_fw:
12738:printer_mib:
12948:raritan:
13083:servertech_sentry3:
13616:synology:
16206:ubiquiti_airfiber:
18054:ubiquiti_airmax:
19361:ubiquiti_unifi:

Prometheusから参照

設定サンプルはSNMP Exporterのページに載っているが、今回はArista EOS用の設定を指定するためparams.module[arista_sw]を記載。

replacementはPrometheusから見たSNMP Exporterのアドレスで、targetsにはSNMP Exporterから見たSNMPエージェント(今回はArista EOSのNW機器)のアドレスを指定する。

以下の内容をscrape_configsに追加。

  - job_name: 'snmp'
    static_configs:
      - targets:
        - 192.168.0.18:45161  # SNMP device.
    metrics_path: /snmp
    params:
      module: [arista_sw]
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 192.168.0.18:9116  # The SNMP exporter's real hostname:port.

Targetsはこの通り、SNMPの設定が追加される。

f:id:zaki-hmkc:20201229143144p:plain

NW機器なので転送量とか見れたらいいんだけどちょっとよくわからなかったので、ぱっと見で値が入ってそうなhrStorageUsedを指定。

f:id:zaki-hmkc:20201229143158p:plain


KubernetesでExporterを動かす場合

github.com

SNMP ExporterのHelmチャートがあるので、これを使うのが楽だと思う。

$ helm install snmp prometheus-community/prometheus-snmp-exporter -n monitoring

Exporterしか動かしてないけどこの通り。

[zaki@cloud-dev ~]$ helm ls -n monitoring 
NAME            NAMESPACE       REVISION        UPDATED                                STATUS   CHART                                   APP VERSION
blackbox        monitoring      1               2020-12-25 14:21:31.954695693 +0900 JSTdeployed prometheus-blackbox-exporter-4.10.1     0.18.0     
snmp            monitoring      1               2020-12-25 17:25:15.075107984 +0900 JSTdeployed prometheus-snmp-exporter-0.1.1          0.19.0     
[zaki@cloud-dev ~]$ kubectl get pod -n monitoring 
NAME                                                     READY   STATUS    RESTARTS   AGE
blackbox-prometheus-blackbox-exporter-6fcb969596-75ffg   1/1     Running   0          4d
snmp-prometheus-snmp-exporter-5b688cff9-2tfg4            1/1     Running   1          3d21h

なお、PrometheusからSNMP Exporterへの疎通と、SNMP ExporterからNWデバイスへの疎通があれば良いので、PrometheusをKubernetes上で動作させていたとしても、SNMP Exporterも同じKubernetesで動作させなければいけないというわけではない。


SNMP Exporterで使用するsnmp.ymlにどんなSNMPの値を扱うかを定義し、Prometheusの設定でどのホストにあるSNMPエージェントの値を参照するか、という構成のようだ。