zaki work log

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

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エージェントの値を参照するか、という構成のようだ。

Arista EOSのcEOS-labコンテナでSNMPサーバー(SNMPエージェント)

とある作業でSNMP接続を試したかったけど手持ちの環境でSNMPサーバーが無かったので、Arista EOSのコンテナ版cEOS-labでSNMPサーバーを有効にして外部SNMPマネージャー(snmpwalk)から値を取得できるようにしてみた。
Linuxサーバーに入れても良かったけど、仕事ではAWS上のCisco CSR(Cloud Service Router)で同じことをしたため、手元のcEOSでも同じことできるのかなーと試してみた。

zaki-hmkc.hatenablog.com

SNMP用161/UDPのpublish設定

SNMPエージェントは161/UDPを使用するため、Dockerコンテナの起動オプションで161/UDPをpublishするように以下のオプションでコンテナ起動。
UDPの場合のpublish設定はポート番号の後ろに/udpを付与する。

docker run --name=ceos-snmp \
  --privileged \
  -p 45022:22 \
  -p 45161:161/udp \
  -e CEOS=1 \
  -e container=docker \
  -e EOS_PLATFORM=ceoslab \
  -e SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT=1 \
  -e ETBA=1 \
  -e INTFTYPE=eth \
  -d \
  ceos:4.21.12M /sbin/init

以下実行結果。

[zaki@cloud-dev image]$ docker run --name=ceos-snmp \
>   --privileged \
>   -p 45022:22 \
>   -p 45161:161/udp \
>   -e CEOS=1 \
>   -e container=docker \
>   -e EOS_PLATFORM=ceoslab \
>   -e SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT=1 \
>   -e ETBA=1 \
>   -e INTFTYPE=eth \
>   -d \
>   ceos:4.21.12M /sbin/init
0380ca057b80ce29cb37d2b469573d095e44ab4fbf31c085cf5986186ce99446

起動したらコンテナのシェルを起動して初期設定(ユーザー作成)を行う。 (前回と同じ内容)

[zaki@cloud-dev image]$ docker exec -it ceos-snmp bash
bash-4.3# Cli
0380ca057b80>enable 
localhost#configure 
localhost(config)#username lab-user privilege 15 secret p@ssword
localhost(config)#exit
localhost#exit
bash-4.3# exit
exit
[zaki@cloud-dev image]$ 
[zaki@cloud-dev image]$ ssh lab-user@localhost -p 45022
The authenticity of host '[localhost]:45022 ([::1]:45022)' can't be established.
ECDSA key fingerprint is SHA256:BVnPKEnu8jkuxHBJxLP225oto+2nI4UWSUe6Km3A4us.
ECDSA key fingerprint is MD5:f6:8d:f5:4b:ca:ed:00:b2:83:a4:a4:50:81:38:50:02.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[localhost]:45022' (ECDSA) to the list of known hosts.
Password: 
localhost>en
localhost#

SNMPの有効化だけであれば、機器設定用のユーザー作ってsshしなくてもdocker exec -itで入った状態でCliしてenableしてconfigureのあとに以下の作業は可能。

SNMPの有効化

SNMPの状態を確認。
(この表示だと有効なんだか無効なんだか。。。)

localhost>show snmp
SNMP agent enabled in VRFs: default
Transmit message maximum size: 65536
SNMP agent disabled: no communities or users configured

SNMPエージェントを有効にする。

localhost>enable 
localhost#configure 
localhost(config)#
localhost(config)#snmp-server community public ro

状態を確認。
(478となっているパケット数は状況によって増える)

localhost(config)#show snmp
478 SNMP packets input
    0 Bad SNMP version errors
    0 Unknown community name
    0 Illegal operation for community name supplied
    0 Encoding errors
    478 Number of requested variables
    0 Number of altered variables
    0 Get-request PDUs
    478 Get-next PDUs
    0 Set-request PDUs
478 SNMP packets output
    0 Too big errors
    0 No such name errors
    0 Bad value errors
    0 General errors
    478 Response PDUs
    0 Trap PDUs
Access Control
    0 Users
    0 Groups
    0 Views
SNMP logging: disabled
SNMP agent enabled in VRFs: default
Transmit message maximum size: 65536

show running-configでも確認。
(includeについてはこちら)

localhost#show running-config | include snmp
snmp-server community public ro

ちなみに(docker exec -itbashシェルを起動して)ポートのListen状態を確認するとこんな感じ。

bash-4.3# ss -anpu
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
UNCONN     0      0                 *:161                           *:*                   users:(("snmpd",pid=2270,fd=7))
UNCONN     0      0                :::161                          :::*                   users:(("snmpd",pid=2270,fd=10))

SNMPエージェントを無効にするには、有効にした時のコマンドの頭にnoを付けて実行すれば良い。(SNMPに限らず機能共通)

localhost(config)#no snmp-server community public ro

snmpwalkの実行

インストール

yumで入れる場合はnet-snmp-utilsパッケージをインストール。(net-snmpだと入らない)
aptの場合はsnmpパッケージで入る。

実行

デフォルトは161/UDPを使用するけど今回は45161/UDPに変更しているので、ホスト名の後ろにポート番号を明示する。

[zaki@cloud-dev ~]$ snmpwalk -v1 -c public localhost:45161
SNMPv2-MIB::sysDescr.0 = STRING: Linux localhost 3.10.0-1160.11.1.el7.x86_64 #1 SMP Fri Dec 18 16:34:56 UTC 2020 x86_64
SNMPv2-MIB::sysObjectID.0 = OID: SNMPv2-SMI::enterprises.30065.1.2600
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (81009) 0:13:30.09
SNMPv2-MIB::sysContact.0 = STRING: 
SNMPv2-MIB::sysName.0 = STRING: 
SNMPv2-MIB::sysLocation.0 = STRING: 
SNMPv2-MIB::sysServices.0 = INTEGER: 14
SNMPv2-MIB::sysORLastChange.0 = Timeticks: (18932) 0:03:09.32
SNMPv2-MIB::sysORID.1 = OID: TCP-MIB::tcpMIB
SNMPv2-MIB::sysORID.2 = OID: UDP-MIB::udpMIB
SNMPv2-MIB::sysORID.3 = OID: SNMPv2-MIB::snmpMIB
SNMPv2-MIB::sysORID.4 = OID: SNMP-VIEW-BASED-ACM-MIB::vacmBasicGroup

:
:

SNMPv2-SMI::mib-2.47.1.1.1.1.2.1 = STRING: "cEOSLab"
SNMPv2-SMI::mib-2.47.1.1.1.1.2.1100004000 = STRING: "Chip Container"
SNMPv2-SMI::mib-2.47.1.1.1.1.2.1100006000 = STRING: "Sensor Container"
SNMPv2-SMI::mib-2.47.1.1.1.1.2.1100140000 = STRING: "Port Container"
SNMPv2-SMI::mib-2.47.1.1.1.1.2.1100300000 = STRING: "Xcvr Slot Container"
SNMPv2-SMI::mib-2.47.1.1.1.1.2.1100600000 = STRING: "Fan Tray Slot Container"
SNMPv2-SMI::mib-2.47.1.1.1.1.2.1100700000 = STRING: "Power Supply Slot Container"

:
:

こんな感じで情報取得できている。

今回はここまで。

関連ドキュメント

試した後にドキュメント探したらあった。

www.arista.com

(memo)ESXiのSNMPエージェント

cEOSでお試ししたあとに「もしや…」と思って見てみたら、ホストの「管理」->「サービス」のリストに「SNMPサーバ」ってあった。ESXiでも使えそう…

[Kubernetes] ConfigMapのおさらい (作成・環境変数・ボリュームマウント)

Kubernetes基本機能の一つであるConfigMapのおさらい。

2022.12.08 Secretについての記事リンク追加 (末尾)

ConfigMapを作る

key=valueの形式をCLIから

--from-literalでkey=valueを指定する。
複数設定可能。

$ kubectl create configmap key-value-sample -n configmap-example \
  --from-literal=SAMPLE_HOST='10.0.0.2' \
  --from-literal=SMAPLE_PORT='25'

--dry-run -o yamlで実行すると以下のConfigMapが生成されることがわかる。

[zaki@cloud-dev configmap (master)]$ kubectl create configmap key-value-sample -n configmap-example \
>   --from-literal=SAMPLE_HOST='10.0.0.2' \
>   --from-literal=SMAPLE_PORT='25' \
>   --dry-run -o yaml
W1227 10:41:53.955673   60645 helpers.go:553] --dry-run is deprecated and can be replaced with --dry-run=client.
apiVersion: v1
data:
  SAMPLE_HOST: 10.0.0.2
  SMAPLE_PORT: "25"
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: key-value-sample
  namespace: configmap-example

key=value形式のファイルから

HOST=10.0.0.2
PORT=25

こういうkey=valueという(デリミタが=になっている)形式になっているファイルであれば、--from-env-fileで指定してConfigMapリソースを作成することができる。

上記の内容のsample.iniファイルがあるとして、以下を実行する。

$ kubectl create configmap key-value-sample -n configmap-example \
  --from-env-file=sample.ini

--dry-runすると以下の通り。

[zaki@cloud-dev configmap (master)]$ kubectl create configmap sample --from-env-file=sample.ini --dry-run -o yaml
W1227 16:17:56.885645  115674 helpers.go:553] --dry-run is deprecated and can be replaced with --dry-run=client.
apiVersion: v1
data:
  HOST: 10.0.0.2
  PORT: "25"
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: sample

以下の形式のファイルだと無効

KEY: VALUE

また、iniファイルのようにセクション名が入っていても無効。

[smtp]
HOST=10.0.0.2
PORT=25

#によるコメントは無視されるので期待通りに処理される。(;によるコメントは無効)

#[config]
HOST=10.0.0.2
PORT=25

ファイルの埋め込み

--from-fileでファイルを指定する。
複数指定可能。

$ kubectl create configmap file-sample -n configmap-example \
  --from-file=/etc/hosts \
  --from-file=/etc/resolv.conf

--dry-run -o yamlを付与して実行すると以下の通り。
ConfigMap内におけるファイル名は作成時に指定するファイル名がそのまま使用される。

[zaki@cloud-dev configmap (master)]$ kubectl create configmap file-sample -n configmap-example \
>   --from-file=/etc/hosts \
>   --from-file=/etc/resolv.conf \
>   --dry-run -o yaml
W1227 10:50:53.399027   63085 helpers.go:553] --dry-run is deprecated and can be replaced with --dry-run=client.
apiVersion: v1
data:
  hosts: |
    127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
  resolv.conf: |
    # Generated by NetworkManager
    nameserver 192.168.0.19
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: file-sample
  namespace: configmap-example

指定ディレクトリ直下の全ファイルを指定

ファイル埋め込みは--from-fileを複数指定で複数ファイル指定が可能だが、--from-fileディレクトリを指定すればそのディレクトリ直下の全ファイルがConfigMapに含めることができる。

$ kubectl create configmap dir-sample -n configmap-example \
  --from-file=${HOME}/work/sample

以下のようなファイルがある状態で、

[zaki@cloud-dev configmap (master)]$ find ~/work/sample
/home/zaki/work/sample
/home/zaki/work/sample/sample1.txt
/home/zaki/work/sample/sample2.txt
/home/zaki/work/sample/.dot-file.txt
/home/zaki/work/sample/subdir
/home/zaki/work/sample/subdir/example.txt

--dry-run -o yamlを付与して実行すると以下の通り。

[zaki@cloud-dev configmap (master)]$ kubectl create configmap dir-sample -n configmap-example \
>   --from-file=${HOME}/work/sample \
>   --dry-run -o yaml
W1227 11:05:36.681042   67278 helpers.go:553] --dry-run is deprecated and can be replaced with --dry-run=client.
apiVersion: v1
data:
  .dot-file.txt: |
    this is dot file.
  sample1.txt: |
    nya-n
    wanwan
  sample2.txt: |
    this file is sample file.
    curry tabetai!
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: dir-sample
  namespace: configmap-example

指定ディレクトリ直下のドットファイルは含まれるが、サブディレクトリは無視される。

ハードタブを含むファイルの場合

YAML内にハードタブは含むことができないため、KYE-VALUE形式と同じように1行で記述され改行やハードタブはエスケープ文字が使われる。

以下はsample2.txtの各行の先頭をハードタブ(\tでインデントした場合)

apiVersion: v1
data:
  sample2.txt: "\tthis file is sample file.\n\tcurry tabetai!\n"
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: dir-sample
  namespace: configmap-example

バイナリファイルの場合

バイナリ使えるのはSecretだけかと思ってたけど、ConfigMapも「UTF-8以外のデータを含む場合はバイナリとみなされBase64エンコード」されてリソースが作成される。

[zaki@cloud-dev configmap (master)]$ ls -l ~/src/usansible/src/icons/usansible.png 
-rw-rw-r--. 1 zaki zaki 5445  9月 26 00:21 /home/zaki/src/usansible/src/icons/usansible.png
[zaki@cloud-dev configmap (master)]$ file ~/src/usansible/src/icons/usansible.png 
/home/zaki/src/usansible/src/icons/usansible.png: PNG image data, 128 x 128, 8-bit/color RGBA, non-interlaced

上記のデータでConfigMapを作ると、

[zaki@cloud-dev configmap (master)]$ kubectl create configmap binary-sample -n configmap-example \>   --from-file=${HOME}/src/usansible/src/icons/usansible.png --dry-run -o yaml
W1227 11:16:56.211264   70269 helpers.go:553] --dry-run is deprecated and can be replaced with --dry-run=client.
apiVersion: v1
binaryData:
  usansible.png: iVBORw0KGgoAAAANSUhEUgAAAI ...
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: binary-sample
  namespace: configmap-example

この通り、PNG画像ファイルがBase64エンコードされてConfigMapが作成される。
マウントすればデコードされて元のデータを使用できる。

YAMLマニフェストで作成

0から作るよりは上記の出力を参考に--dry-run -o yamlの結果をファイルリダイレクトすると簡単に作れる。

注意点として、dataに指定できるのは文字列のみのため、ポート番号のように数値(m/[0-9]+/)のみの指定になる場合はクォートで囲まないとYAML的に数値とみなされてエラーとなる。

ConfigMapをPodから参照する

環境変数として参照

apiVersion: v1
data:
  SAMPLE_EDITOR: emacs
  SAMPLE_SERVER: 10.0.0.2
  SAMPLE_PORT: '8080'
  SAMPLE_USERNAME: curry
kind: ConfigMap
metadata:
  name: configmap-key-value

こんなConfigMapがあるとして、、

ConfigMap内データ全てを環境変数として使用する

key=valueとして作成したConfigMapを環境変数に使用するには、対象のcontainers定義で以下のようにenvFrom:を指定すれば、対象ConfigMapの全データが環境変数としてコンテナに設定される。

configmap-key-valueという名前のConfigMapリソースを参照する場合は以下の通り。

      containers:
      - image: httpd
        name: httpd
        envFrom:
        - configMapRef:
            name: configmap-key-value

ConfigMapの指定キーのみを環境変数で使用する

全てのキーでなく、指定キーのみを参照したい場合は、env.[].valueFromを使用する。

configmap-key-valueというConfigMap内にあるSAMPLE_EDITORのみを使用する場合は以下の通り。
以下では、更に環境変数のキー名をコンテナ上ではPICKUPED_CONFIGMAP_KEYVALとして使うような内容になっている。(env.[].nameは必須キー)
(configMapKeyRef.keyでConfigMapの指定キーを取り出して、env.[].nameの名前で環境変数名としている、ということ)

      containers:
      - image: httpd
        name: httpd
        env:
        - name: PICKUPED_CONFIGMAP_KEYVAL
          valueFrom:
            configMapKeyRef:
              name: configmap-key-value
              key: SAMPLE_EDITOR

全体はこんな感じ

github.com

(参考) ConfigMapを使わない環境変数定義

Deploymentの場合はこんな感じ。

    spec:
      containers:
      - image: httpd
        name: httpd
        env:
        - name: REMOTE_SERVER
          value: "10.0.0.2"
        - name: REMOTE_PORT
          value: "80"

注意点として、前述のYAMLでConfigMap作成時と同様に、環境変数で指定する値は全てYAML的には文字列である必要があるため、ポート番号のような値もYAMLでは文字列として記述しなければならないのでクォートで囲む必要がある。

上の定義でREMOTE_PORTの値の80でクォートが無いとエラーになる。

[zaki@cloud-dev deploy-env (master)]$ kubectl apply -f sample-http.yaml -n zzz 
Error from server (BadRequest): error when creating "sample-http.yaml": Deployment in version "v1" cannot be handled as a Deployment: v1.Deployment.Spec: v1.DeploymentSpec.Template: v1.PodTemplateSpec.Spec: v1.PodSpec.Containers: []v1.Container: v1.Container.Env: []v1.EnvVar: v1.EnvVar.Value: ReadString: expects " or n, but found 8, error found in #10 byte of ...|,"value":80}],"image|..., bigger context ...|value":"10.0.0.2"},{"name":"REMOTE_PORT","value":80}],"image":"httpd","name":"httpd"}]}}}}
|...

ファイルシステムとしてマウント

前提としてConfigMapのデータをファイルシステムとしてマウントする場合はread onlyになる。
ボリュームやマウント関連の操作については基本的にはConfigMap特有の話ではなく、PersistentVolumeを使ったりする場合も同様。

また、ConfigMapのデータをPodからマウントしている状態で、ConfigMapのデータを更新した場合は、デフォルト60秒間隔で更新処理がKubernetesによって行われるため、Podの再作成無しにファイルの更新が反映される。

Mounted ConfigMaps are updated automatically
日本語ドキュメント マウントされたConfigMapは自動的に更新される

※ これはあくまでPod/コンテナにマウントされたファイルがConfigMapの更新によって動的に行われるのであって、コンテナ上で動いているプロセスが更新されたファイルを認識するかはプロセス次第。例えばApache Webサーバーなんかがhttpd.confの変更を再起動無しで認識するか?という話。

ただし、subPath使用時にはこの動的反映は機能しない。(後述)

apiVersion: v1
data:
  sample1.txt: |
    nya-n
    wanwan
  sample2.txt: |
    this file is sample file.
    curry tabetai!
kind: ConfigMap
metadata:
  name: configmap-sample-data

例として上記のConfigMapがあるとして、、

ConfigMap内の全ファイルをマウント

上記ConfigMapの全データ(sample1.txtsample2.txtの両方)をマウントするには、例えばDeploymentの定義であれば、

    spec:
      containers:
      - image: httpd
        name: httpd
        volumeMounts:
        - name: configmap-volume
          mountPath: /var/tmp/configmap
      volumes:
      - name: configmap-volume
        configMap:
          name: configmap-sample-data

containersの定義でマウントするパスとボリューム設定名を記述。
volumesの定義で↑で使用するボリューム設定の実態として対象ConfigMapの名前(上記のconfigmap-sample-data)を指定する。

[zaki@cloud-dev configmap (master)]$ kubectl exec -it -n cm-mount cm-mount-app-6474cfd495-cw6lj -c httpd -- bash
root@cm-mount-app-6474cfd495-cw6lj:/usr/local/apache2# ls /var/tmp/configmap/
sample1.txt  sample2.txt
root@cm-mount-app-6474cfd495-cw6lj:/usr/local/apache2# cat /var/tmp/configmap/sample1.txt 
nya-n
wanwan
root@cm-mount-app-6474cfd495-cw6lj:/usr/local/apache2# cat /var/tmp/configmap/sample2.txt 
this file is sample file.
curry tabetai!

この通り、sample1.txt, sample2.txt共にmountPathに指定したディレクトリにConfigMapのデータがファイルシステムとしてマウントされることで参照できる。

ConfigMapの指定ファイルのみマウント

同じConfigMapのうち、sample1.txtだけを参照したい場合(sample2.txtは参照させない場合)は、volumes定義部分でitemsを使ってキーを指定する。

      volumes:
      - name: configmap-volume1
        configMap:
          name: configmap-sample-data
          items:
          - key: sample1.txt
            path: data1

※ container側は変更無し。

    spec:
      containers:
      - image: httpd
        name: httpd
        volumeMounts:
        - name: configmap-volume1
          mountPath: /var/tmp/configmap

この設定でデプロイすると、以下のように、「configmap-sample-dataリソース内のsample1.txtキーのデータをdata1というファイル名でマウント」という動きになる。
環境変数で指定キーのみの場合と同じように、ConfigMapにおけるキー名がそのままファイル名とはならない動作となる。(もちろんpathにキー名と同じ文字列を指定すればConfigMap定義上のファイル名でマウントできる)

[zaki@cloud-dev configmap (master)]$ kubectl exec -it -n cm-mount file-mount-9c8d8f9df-twd9l -c httpd -- bash
root@file-mount-9c8d8f9df-twd9l:/usr/local/apache2# ls /var/tmp/configmap/
data1
root@file-mount-9c8d8f9df-twd9l:/usr/local/apache2# cat /var/tmp/configmap/data1 
nya-n
wanwan

github.com

subPathを使ったマウント

Using subPath

通常のボリュームマウントでは、対象のルートディレクトリをmountPathのパスにマウントする。なので対象ストレージの全体がマウントされる。
ストレージの全体は不要で、サブディレクトリ以下だけが欲しい場合はsubPathを使うことで、サブディレクトリ以下をマウントすることもできる。

ConfigMapの場合もこの機能を使うことができ、でもConfigMapではディレクトリの概念がないため、「マウントポイントにファイルをマウント」という動作になる。

文書だとよくわからないので実際に(比較のために1Pod複数コンテナ構成で)やってみると、

    spec:
      containers:
      - image: httpd
        name: httpd
        volumeMounts:
        - name: configmap-volume
          mountPath: /var/tmp/configmap/nyan
          subPath: sample1.txt
      - image: centos:7
        name: centos7
        command: [ "tail", "-f", "/dev/null"]
        volumeMounts:
        - name: configmap-volume
          mountPath: /tmp/configmap-file/wan
          subPath: sample1.txt
      volumes:
      - name: configmap-volume
        configMap:
          name: configmap-sample-data

containers.[]volumeMounts定義にsubPath:でパスを指定。このパスというのはボリューム内のパスを指定するが、ConfigMapではファイルしかないのでConfigMap内のキー(つまりファイル名)指定となっている。

この設定でどうなるかというと、sample1.txtのファイルの内容がhttpdコンテナ内では/var/tmp/configmap/nyanファイルに、centos7コンテナ内では/tmp/configmap-file/wanファイルにマウントされるという動作になる。

[zaki@cloud-dev configmap (master)]$ kc exec -it -n cm-mount cm-mount-subpath-64fd77d994-czddj -c httpd -- bash
root@cm-mount-subpath-64fd77d994-czddj:/usr/local/apache2# cat /var/tmp/configmap/nyan 
nya-n
wanwan
[zaki@cloud-dev configmap (master)]$ kc exec -it -n cm-mount cm-mount-subpath-64fd77d994-czddj -c centos7 -- bash
[root@cm-mount-subpath-64fd77d994-czddj /]# cat /tmp/configmap-file/wan 
nya-n
wanwan

subPath指定時の動作として、ConfigMapの内容が更新されてもコンテナ内には反映されないので注意。

github.com


ストレージ上に/path/to/fileという構成のファイルがあったとすると、mountPath/var/tmp/hogeを指定すると、コンテナ内では/var/tmp/hoge/path/to/fileというディレクトリ構成になる、というのが通常の使い方。
subPathの本来の使い方としては、要件として/path/toというディレクトリが不要でfileだけを参照したい場合(任意のパスにfileを置きたい場合)に、subPath: path/toを記述することで、コンテナ内で/var/tmp/hoge/fileというパスで参照できるようになる、というもの。


一つのConfigMapデータに対してコンテナ毎にファイル名を変更して参照したいのであれば、記述量は増えるがitemsを使って別々のpathによるファイル名設定を行い、それぞれのボリューム設定をマウントする方が(ConfigMap更新によるファイル変更を検知できるという点では)良い。
逆にデプロイ時のConfigMapの内容から不用意に変更されたくないような使い方をしたい場合は有効。

      volumes:
      - name: configmap-volume1         # "wann"というファイル名を使いたいコンテナはこれを使う
        configMap:
          name: configmap-sample-data
          items:
          - key: sample1.txt
            path: wann
      - name: configmap-volume2         # "nyan"というファイル名を使いたいコンテナはこれを使う
        configMap:
          name: configmap-sample-data
          items:
          - key: sample1.txt
            path: nyan

なお、値を不変にしたいという目的であれば、Kubernetes 1.19時点ではbetaの「Immutable ConfigMaps」機能もある。(動作未検証)


そういえば--dry-runじゃなくて--dry-run=clientって書かないといけなかったのか。忘れてた。


環境

[zaki@cloud-dev configmap (master)]$ kubectl version --short
Client Version: v1.20.1
Server Version: v1.19.4

関連ドキュメント

kubernetes.io

kubernetes.io

(2022.12.08追記) Secretについても以下で少し作成

zaki-hmkc.hatenablog.com