てくなべ (tekunabe)

ansible / network automation / 学習メモ

2018年の個人的ふりかえり(アウトプット編)

はじめに

今年は社外での発表やブログなどのアウトプットを強化してきました。

あとで自分が見たときに「お~」と言ってみたいのでリストにしてみます。(参考: 去年2017年版

だいぶ Ansible が多いです。


■ 2018年発表スライド

Ansible 関連

www.slideshare.net

www.slideshare.net

www.slideshare.net

www.slideshare.net

www.slideshare.net

www.slideshare.net

www.slideshare.net

www.slideshare.net

www.slideshare.net

www.slideshare.net

www.slideshare.net

www.slideshare.net

www.slideshare.net

ネットワーク関連

www.slideshare.net

広報関連

www.slideshare.net

キャリアなど

www.slideshare.net www.slideshare.net www.slideshare.net


■ 2018年ブログ投稿(118記事)

一覧 tekunabe.hatenablog.jp

(日付昇順)


■ 2018年書籍

gihyo.jp

■ 番外編

社内エンジニアの社外アウトプットも促進できました。 qiita.com

tekunabe.hatenablog.jp


おわりに

来年も、

  • 日本語情報が少ないもので役立ちそうなもの
  • あとで自分がみて参考になりそうなもの

を中心に積極的にアウトプットしていきたいと思います。

2018年の個人的ふりかえり(越境編)

はじめに

2018年は私にとっての越境の年でした。

今までは越境という言葉はあまり使いませんでした。 しかし、今年「カイゼン・ジャーニー たった1人からはじめて、「越境」するチームをつくるまで」という本に出会い、各種イベントに参加したり、コミュニティ(越境者の集い)に関わっていくうちに、越境という言葉を意識するようになりました。

www.shoeisha.co.jp

f:id:akira6592:20181226133713j:plain:w300
パワーをくれるコミュニティの仲間たち
2018年の自身のふりかえりを、大げさなところありますが越境(△△から○○へ)という切り口でまとめたいと思います。


■ ブログから紙へ

Software Design 寄稿

Software Design 2018年12月号の Ansilbe 特集の記事を寄稿させていただきました。 もともと文章が書くのが得意ではないうえに、紙媒体、雑誌ならではの難しさもあったのですが、とても良い経験になりました。

tekunabe.hatenablog.jp


■ 参加から企画・運営へ

今までは、イベントに参加するだけだったのですが、少し企画や運営にも回ることができました。

Ansible もくもく会の誘致

東京近郊の本イベントとしては、初のレッドハットさん以外での開催となりました。 tekunabe.hatenablog.jp

自社のアドベントカレンダー

全社員に告知して巻き込みました。 tekunabe.hatenablog.jp


■ 日本語のみから英語へ

翻訳

Ansible の公式ブログに、ネットワークモジュールの最近のアップデートをまとめた記事「RED HAT ANSIBLE NETWORK AUTOMATION UPDATES」があったので、各方面に許可を頂いて翻訳させていただきました。

記事の筆者は、Ansible Automates Tokyo 2018 でお会いした Sean Cavanaugh さん でした。

(...略...)

翻訳記事はこちらです。

tekunabe.hatenablog.jp

英語記事

翻訳とは逆に、英語の記事にも挑戦しました。といってもほとんど例とログだけで、解説が不要なレベルのものですが・・・。

tekunabe.hatenablog.jp


■ ITエンジニアから広報へ

今まではITエンジニア向けのイベントしか参加、登壇したことなかったのですが、他職種のイベントにも行ってみました。

PRLT

広報LT大会#PRLT (connpass / note) という、広報PRに関わる悩みや事例をシェアするコミュニティのイベントに LT を発表させていただきました。

職種間で連携するといいことがある、自身の職種にとって当たり前のことでも他職種からみたら新鮮で素晴らしいことも多い、ということが少しでも伝わったようで良かったです。

また、エンジニアが集まるイベントとは異なる雰囲気も感じて刺激になりました。

tekunabe.hatenablog.jp


■ 利用から貢献へ

はじめての issue 、プルリク

OSSに関しては、いままで利用する一方だったのですが、今年は issue を出したりコメントしたりプルリクを出したりしました。まだまだちょっとしたことしかできていませんが、一歩だけ踏み出した年になりました。

tekunabe.hatenablog.jp

JANOG 42 の「OSS光と闇」というセッションで聞いた、以下のポイントも心に残りました。


■ 東京から各地へ

いままで、コミュニティのイベントは東京近郊で開催されるものしか参加・登壇してこなかったのですが、今年は三重、大阪、広島、福岡におじゃましました。

広島: JANOG41 Meeting in Hiroshima

tekunabe.hatenablog.jp

tekunabe.hatenablog.jp

三重: JANGO42 Meeting in Mie

tekunabe.hatenablog.jp tekunabe.hatenablog.jp

福岡: Ansible Night in Fukuoka 2018.08

tekunabe.hatenablog.jp

大阪: Ansible Night in Osaka 2018.11

togetter.com


■ 既存職種から新規職種へ

新設: テクニカルエバンジェリスト

上長や人事の協力のもと、自分のやりたかったことを新しい職種として新設してもらいました。今年中にこの職種の二人目が登場した点も嬉しかったです。

https://www.slideshare.net/akira6592/jinnjiteki-kaizen-journeylt


■ 100超え

ブログ記事が年間100を超える

去年2017年4月から開始したこのブログ。1年を通して書いたのは今年が初めてなのですが、この記事投稿時で 111 となりました。

キリがいい数字を超えるとなんとなく達成感がありますね。

tekunabe.hatenablog.jp


まとめ

今でこそ意識付けとして越境という言葉をよく使いますが、これらの行為が息を吸うようにごく自然となり、自分に向けて使わなくなるかもしれません。それはある意味では正常な成長だと思っています。

はじめて自社のアドベントカレンダーを企画したらアウトプッターが集まった話

はじめに

2018年、自社ではじめてアドベントカレンダーを企画し、枠をすべて埋めた形で無事に終了しました。

f:id:akira6592:20181225155831p:plain:w300
埋まった

qiita.com

この記事では、企画の経緯や感じたことなどをまとめたいと思います。

アドベントカレンダーは本来、クリスマスまでの期間に日数を数えるために使用されるカレンダーのことです。この習慣に乗っかるかたちで、記事を投稿しあうイベントで https://qiita.com/advent-calendarhttps://adventar.org/ などが有名です。

そろそろウチでも・・

弊社でもここ数年、ブログやイベント登壇などによって技術情報を社外にアウトプットする人たちが増えてきました。そのため「そろそろ、ウチでもアドベントカレンダーいけるんじゃないか?」と思って9月ごろに考えました。

全社員にお知らせ

社内数人に少し相談したところ好感触だったので、10月末に全社員にメールなどを通じて企画をお知らせしました。

伝えた内容は以下の通りです(概要)

  • どうやって参加するの?
    • Qiita アカウントでログインして https://qiita.com/advent-calendar/2018/ap-com の12月1日から25日までの 空いている日にちに登録
    • Organization に所属してなくても参加OK
    • この機会に Organization に所属した場合は連絡を
    • 記事自体は外部のブログでもOK
  • なぜ開催するの?
  • アウトプットするメリットは?
    • (予想以上の反響があったり疑問点が解消したりして、さらにアウトプットしたい、勉強したい、という気持ちになった新卒2年目の事例を紹介)
  • どんなネタにしたら良い?
    • プログラミングでもサーバーでもネットワークでもOK、あくまで技術中心
    • その他、相談ごとなどあったらお気軽に私まで

わりと順調に枠が埋まっていく

企画のお知らせの後、少しずつ枠が埋まっていきました。普段あまり接点がない人でも技術的な興味が似通っているという発見もありました。

投稿開始~終了

多くの人が、あらかじめ記事を書いて下書き、予約といった準備をしていたようで、12/01 の開始以降も順調に投稿されていきました。

途中、広報や上層部からも注目され、宣伝してもらったので社内認知度も上がったと思います。

しばらく空いたままの枠もありましたが、2枠目を登録する人によって助けられました。私自身でも2枠目3枠目を埋めることはできましたが、なるべく多くの人に参加してもらいたかったため自粛しました。

そして 12/25 に最後の投稿がされて、無事に全部埋まった形で終了しました。

記事一覧

以下の記事が投稿されました。

日付 タイトル
12/01 [Ansible] グループ変数を活用したネットワーク機器への Syslog サーバー追加
12/02 Ansibleを実際使ってみてハマったハマりポイント
12/03 VSCode内の統合ターミナルから、Docker内でアプリを実行するために使った力技
12/04 VBAでOutlookの仕様『宛先へ付与される「’」』を表示名ごと削除する
12/05 Drag & Drop API を使うときにやっておいたほうがいいこと
12/06 Terraformを使ってEKSを構築する
12/07 外部からVPC内に接続するまでの経路が途切れてしまったときの緊急対処法
12/08 Golangでインターフェースを使いコードを疎結合にする
12/09 React 16.6の新機能、React.lazyとReact.Suspense を使った非同期処理
12/10 React v16.x ロードマップをかいつまんでみた
12/11 ネットワークエンジニアがMacで業務するために必要なツールたちまとめ
12/12 Outlook VBAで定型項目の通知メールを自動転記する
12/13 VagrantにてOpenstack&TungstenFabricを一発構築してみた(作業:1分未満、待ち:数十分)
12/14 Ansible_そういえば、よく使ったねモジュールランキング2018
12/15 Zabbix4.0のインポートファイルで設定出来ない項目を一括変更する方法
12/16 Cloud AutoML VisionによるCiscoルータの分類 ~画像データ水増し効果の推定~
12/17 VagrantによるVXLAN設定環境一発構築
12/18 WordPress環境を作る Kubernetes編
12/19 Kerasの学習済みモデルを使ってサクッとWebアプリ作成
12/20 Rancher を使ってCloudProviderを使ってみた(on AWS)
12/21 EC2インスタンスの状態変化をLambda(boto3)とSNSで自動通知する
12/22 PackerのAnsibleProvisionerでインベントリ毎のgroup_varsを使い分ける
12/23 Kubernetesで監視を行う際の注意点
12/24 ST2をslackでコントロールする
12/25 [StackStorm] mysql Pack 使ってみた

終わってみて

アウトプットする人たちを引き出して可視化できた

今回のアドベントカレンダーという、アウトプットする場を用意することで、アウトプットする人たちを引き出せました。年内までの社内の技術研修のアウトプット先としてもちょうどよかったようです。

眺めるみると、この人は乗っかってくれるだろう、という人もいれば、意外な人もいて興味深かったです。「あの人 Qiita やってたんだ!」のような。いちエンジニア社員の呼びかけで、集まってもらって嬉しかったです。

[Ansible] 公式 lint ツール、ansible-lint 4.0.0 リリース。新しいドキュメントサイトやデフォルトルール追加など

■ はじめに

Ansible Playbook の 書式をチェックする lint ツール ansilbe-lint のバージョン 4.0.0 が 2018/12/18 にリリースされました。

  • 以下 changelog から引用
    • New documentation site docs.ansible.com/ansible-lint
    • Additional default rules for ansible-lint, listed in docsite default rules
    • Fixed running with role path containing single or multiple dirs #390
    • Fixed double sudo rule output #393
    • Severity property added to rules to be used by Galaxy #379
    • Packaging: consistency and automation #389
    • Updated rule TrailingWhitespaceRule.py to remove carriage return char #323
    • Allow snake_case module names for rules #82
    • Suggest tempfile module instead of mktemp command #422
    • Update tox to run with only supported ansible versions #406
    • GitHub repository edits: move to ansible org, add CODE_OF_CONDUCT, add ROADMAP, label edits

インストールやアップグレードは、今までと同じく「pip install ansible-lint」、「pip install --upgrade ansible-lint」でできます。

この記事では、個人的にきになった、新しいドキュメントサイトと、追加されたデフォルトルールについてまとめます。


■ 新しいドキュメントは doc.ansible.com 配下に

https://docs.ansible.com/ansible-lint/ が新しいドキュメントのサイトです。

f:id:akira6592:20181224125025p:plain

ansible-lint はいままで、willthames/ansible-lint というリポジトリで開発されていましたが、2018年10月に ansilbe 配下の ansilbe/ansible-lint に移動しました。これでドキュメントもコードも ansible 配下ということになりました。

なお、現在では ansible 本体のドキュメント上では、anislbe-lint は

the official, highly configurable best-practices linter for Ansible playbooks, by Ansible.

と記載されています。


■ デフォルトルールの追加

ansible-lint 4.0.0 で 14 個のデフォフォルトルールが追加されました

ここでは、今回追加されたデフォルトルールの中で、気になったルールをいくつか確認してみます。

Deprecated module は使わないこと

Ansible では、将来利用不可になるなどの利用で、利用が推奨されていないモジュールがあります。ドキュメント上では、Deprecated module と表現されます。このルールでは、Deprecated module を使用を禁止します。

{{ var_name }} の変数名の前後にはスペースを入れること

  • [E206] Variables should have spaces before and after: {{ var_name }}

Plaubok 内では、変数名を {{ }} で囲いますが、その際、変数名の前にスペースを入れる、というルールです。たまに、スペースがない記載を見かけます。

ロール内の copy や template の src には ../templates を指定しないこと

  • [E404] Doesn’t need a relative path in role

(Playbookではなく)ロール内で使用する、copy や template モジュールの src オプションで、../templates を含む相対パスを使用しない、というルールです。 もともとロールの仕組みとして、明示的な指定をしなくてもロールから見て ../templates/ 配下のファイルを参照する仕様なので、書き方を統一させるいみでのルール追加なのだと思います。

なお、コードを見る限りcopytemplates モジュールの他に、win_copywin_templates モジュールも、このルールの対象のようです。

リテラルと True/False を比較しないこと

  • [E601] Don’t compare to literal True/False

when: var == True ではなく、when: var で、

when: var == Flase ではなく、when: not var で、

というルールです。

空文字を比較しないこと

  • [E602] Don’t compare to empty string

when: var != "" ではなく、when: var で、

when: var == "" でなく、when: not var で、

というルールです。

補足

もちろん、今まで通りデフォルトルールを除外したり(コンフィグファイル内の skip_list )、逆に新しいルールを追加することもできます。

まとめ

これまで、複数の書き方が許容されていたものなどに対して、新たなルールが設けられました。 公式の linter である ansible-lint のデフォルトルールが追加されていくことで、一つの書き方に緩やかに収束していくのだと思います。

[Ansible] ios_config モジュールの backup オプションによるバックアップで意図しないバックアップファイルが削除されるバグについて (Ansible 2.7.5 現在)

■ はじめに

Ansible には Cisco IOS ネットワーク機器に設定変更コマンドを実行する ios_config モジュールがあります。 ios_config モジュールには、backup という、設定変更前のコンフィグを事前に Ansible 側に保存するオプションがあります。 デフォルトは no のため、バックアップは保存しません。backup: yes と指定するとバックアップが有効になります。

この backup オプションによるバックアップには、ターゲットホスト名が類似(例: ios01ios01a)した複数のホストを対象にすると、バックアップファイルが他ホストによって上書きされてしまう、というバグがあります。(Ansible 2.7.5 現在)

現在 devel ブランチで開発中の Ansible 2.8 では修正されています。

この記事では、本バグについての発生条件、再現性の確認、修正後の動作確認についてご紹介します。

(2019/02/08 追記) Ansible 2.7 は 2.7.7 で本バグが修正されたようです。

Change backup file globbing for network _config modules so backing up one host's config will not delete the backed up config of any host whose hostname is a subset of the first host's hostname (e.g., switch1 and switch11)

ansible/CHANGELOG-v2.7.rst at stable-2.7 · ansible/ansible · GitHub


■ 原因

バックアップする直前に、過去のバックアップファイルを削除します。 削除対象のファイル名を決める際、ホスト名の 前方一致 でマッチさせているため、意図しないホストのバックアップファイルまで消してしまいます。

そのため、例えば ios01aios01 の 2ホスト対してバックアップを行う場合、ios01aios01 の順にのタスクが実行されると、以下のようになってしまいます。

  • ios01a のタスク
    • ios01a の前方一致でマッチしたファイルを削除(特に問題なし)
    • ios01a のバックアップファイルを保存する
  • ios01 のタスク
    • ios01 の前方一致でマッチしたファイルを削除(先程保存したios01a のバックアプファイルが削除されてしまう)
    • ios01 のバックアップファイルを保存する


■ 再現の確認(Ansible 2.7.5)

バグが発生することを確認してみます。

インベントリファイル (inventory.ini)

先程例に示した、2ホストを定義して、グループ ios でまとめます。

[ios]
ios01 ansible_host=ios-xe-mgmt.cisco.com 
ios01a ansible_host=ios-xe-mgmt.cisco.com

変数ファイル (gorup_vars/ios.yml)

グループ ios が利用する変数を定義します。特に今回のバグに関係するものはありません。

ansible_network_os: ios
ansible_user: user
ansible_password: pass9999
ansible_connection: network_cli

playbook (backup_test.yml)

グループ ios を対象にして ios_config モジュールの backup オプションによる、バックアップのみ行う Playbook にします。

- hosts: ios
  gather_facts: no

  tasks:
    - name: backup test
      ios_config:
        backup: yes

実行結果

ここでは分かりやすいように、ホストごとに ansible-playbook コマンドを分けて実行します。

1つめのホスト(ios01a)

まずは、ios01a に対して実行します。

$ ansible --version
ansible 2.7.5
(...略...)

$ ansible-playbook -i inventory.ini backup_test.yml -l ios01a

PLAY [ios] *************************************************************************

TASK [backup test] *****************************************************************
ok: [ios01a]

PLAY RECAP *************************************************************************
ios01a                     : ok=1    changed=0    unreachable=0    failed=0

バックアップファイルは backup ディレクトリの中に保存される(なければ作成される)ので、backup ディレクトリ内を確認します。

$ tree backup
└── ios01a_config.2018-12-22@10:37:25

正常に、ios01a のバックアップファイルが保存されました。

2つめのホスト (ios01)

続いて、ios01 に対して実行します。

$ ansible-playbook -i inventory.ini backup_test.yml -l ios01

PLAY [ios] *************************************************************************

TASK [backup test] *****************************************************************
ok: [ios01]

PLAY RECAP *************************************************************************
ios01                      : ok=1    changed=0    unreachable=0    failed=0

先ほどと同じく、backup ディレクトリを確認します。

$ tree backup
backup
└── ios01_config.2018-12-22@10:38:26

このように、1つめのホスト ios01a のバックアップファイルが消えてしまいios01 のバックアップファイルのみになってしまいました。バグが再現しました。

本来であれば以下のように、 ios01ios01a の 2ホスト分のバックアップアップファイルがあるべきところです。

$tree backup
backup
├── ios01_config.2018-12-22@10:38:26 
└── ios01a_config.2018-12-22@10:37:25   # このファイルが消えてしまった


■ 開発中の Ansible 2.8 での修正確認

本バグの修正コードは、devel ブランチ(開発中のAnsible 2.8)にマージされています。修正されていることを確認するために、実際に試してみます。

先程用意したインベントリファイルや Playbook など一式はそのまま利用します。backup ディレクトリは一旦削除しておきます。

1つめのホスト(ios01a)

まずは、ios01a に対して実行します。

$ ansible --version
ansible 2.8.0.dev0
(...略...)
$  ansible-playbook -i inventory.ini backup_test.yml -l ios01a

PLAY [ios] *************************************************************************

TASK [backup test] *****************************************************************
ok: [ios01a]

PLAY RECAP *************************************************************************
ios01a        : ok=1    changed=0    unreachable=0    failed=0    skipped=0

backup ディレクトリを確認します。

$ tree backup
backup
└── ios01a_config.2018-12-22@10:51:28

正常に ios01a のバックアップファイルが保存されました。ここまでは、Ansible 2.7.5 で試したときと同じです。

2つめのホスト(ios01)

続いて、ios01 に対して実行します。

$  ansible-playbook -i inventory.ini backup_test.yml -l ios01

PLAY [ios] *************************************************************************

TASK [backup test] *****************************************************************
ok: [ios01]

PLAY RECAP *************************************************************************
ios01         : ok=1    changed=0    unreachable=0    failed=0    skipped=0

backup ディレクトリを確認します。

$tree backup
backup
├── ios01_config.2018-12-22@10:52:35
└── ios01a_config.2018-12-22@10:51:28

今回(開発中の Ansible 2.8)は、無事に 2つのホスト分のバックアップファイルがある状態になりました。


IOS 以外のモジュールにも同様のバグ

修正のPRを確認すると分かりやすいのですが、他のプラットフォームのモジュールにも同じような、ホスト名の前方一致で削除対象のファイル名を決めている箇所があったようです。

junos_config モジュールの backup オプションだけ試したのですが、やはり Ansible 2.7.5 で同じバグが再現でき、開発中の Ansible 2.8 で修正されていることを確認しました。


■ まとめ

Ansible 2.7.5 現在、cisco_ios モジュールの backup オプションのコンフィグファイルのバックアップの処理で、ターゲットホスト名が類似していると、意図せずファイルが削除される場合があることを確認できました。 このバグは Ansible 2.8 で修正される予定です。また、Ansible 2.7 系のブランチにもバックポートされそう(2018/12/22現在)なので、Ansible 2.7.6 で修正されるかもしれません。

参考

対応 issue

github.com

対応 PR

devel ブランチ

github.com

stable-2.7 ブランチ

github.com

[Ansible] ios_config モジュールの src で指定するファイルの内容には正しいインデントが必要


これは Ansible Blogger 2018 (sponsored by Red Hat) Advent Calendar 2018 の22日目の記事です。

■ はじめに

Ansible には、 Cisco IOS ネットワーク機器に対して設定変更コマンドを実行できる ios_config モジュールがあります。

ios_config モジュールには、Playbook 内に実行したいコマンドを直接指定する lines オプションのほかに、src オプションがあります。 src には、別ファイルにコマンドを書いて(以下、コマンドファイル)、そのファイル名を指定します。 コマンドファイルはただコマンドを列挙するだけでなく、階層を考慮して適切にインデントする必要があります。

f:id:akira6592:20181222130211p:plain
正しいインデントが必要

この記事では、どのようにインデントすればよいのか、なぜインデントが必要なのか、についてご説明します。


■ まとめ

少し長くなるので、はじめにまとめます。

  • ios_config モジュールの src で指定するコマンドファイルは階層を示すためにインデントが必要
  • インデントの形式はネットワーク機器側の show run で表示される形式と同じ
  • インデントがない場合、すべてグローバルコンフィギュレーションモードで実行しようとしてしまう
  • インデントがある場合、コンフィグの階層を解釈したうえで正しく実行する


■ そもそも src オプションとは

はじめにでもご紹介した通り、コマンドファイルを指定するオプションです。

  • Playbook
- hosts: iosprg
  gather_facts: no

  tasks:
    - name: config test
      ios_config:
        src: config.txt    # ファイル名を指定
  • config.txt
interface GigabitEthernet3
 description TEST
 ip address 172.16.0.1 255.255.255.0

今回は、このコマンドファイルのインデントが重要というお話です。


■ どのようにインデントすればよいのか

以下の点だけおさえておけば大丈夫です。

ネットワーク機器側の show running-config で表示される形式と同じインデントをする

例えば、具体的には以下のような形式です。見慣れていると形式かと思います。

  • インデントあり
interface GigabitEthernet3
 description TEST
 ip address 172.16.0.1 255.255.255.0

コンフィグの階層をインデントで表したような形式です。

interface GigabitEthernet3 が、グローバルコンフィギュレーションモードで実行したいコマンドです。 スペースによってインデントされている description TESTip address 172.16.0.1 255.255.255.0 が、インターフェースコンフィギュレーションモードで実行したいコマンドです。


■ だめな書き方

一方、コンフィグの階層を作らずにインデントなしで以下のように書いてしまうと、Playbook 実行時にエラーが起こるなどの不都合が起きてしまいます。

  • インデントなし
interface GigabitEthernet3
description TEST
ip address 172.16.0.1 255.255.255.0

手作業でターミナルへコピペする分には、上記のようにインデントなしでも問題ありません。 すべてのコマンドを実行し、1行ごとにネットワーク機器側が必要に応じてモードを切り替えるためです。 (もちろん手作業の場合でも、見さすさの観点でインデントを入れるケースもよくあると思います。)

しかし、Ansible の ios_config モジュールの src で指定するコマンドファイルにおいては、コマンドの階層を考慮して適切にインデントする必要があります。

その理由についてご説明します。


■ インデントが必要な理由

ios_config モジュールは、コマンド実行前に、そのコマンドがネットワーク機器側に設定済みかどうか確認し、設定済みであれば実行しない、という仕組みです。設定済みかどうか確認する際に、コンフィグの階層まで含めて比較、確認します。

f:id:akira6592:20181217133821p:plain
階層を解釈して比較し差分を生成

階層まで含めて正しく比較するために、コマンドファイルのインデントが利用されます。

具体的な例で見ていきましょう。

インデントがないとコンフィグ階層が解釈されない

インデントがないと、ios_config モジュールはすべてグローバルコンフィグレーションモードで実行しようとします。その際、ネットワーク機器側のグローバルコンフィギュレーションレベルで設定済みかどうかを確認します。そして、設定済みのコンフィグはスキップされ実行しません。

例えば、もし

interface GigabitEthernet3
 no ip address

という状態のネットワーク機器に対して、

interface GigabitEthernet3
description TEST
ip address 172.16.0.1 255.255.255.0

というインデントがないコマンドファイルを src に指定して実行すると、 interface GigabitEthernet3 がスキップされて実行されません。 続いて、グローバルコンフィギュレーションモードのまま description TEST を実行しようとしてエラーになります。グローバルコンフィギュレーションモードでは desctiption コマンドが利用できないためです。

f:id:akira6592:20181217120626p:plain
インデントがない場合すべてグローバルレベルで比較

  • ansible-paybook コマンド実行時エラー抜粋
"module_stderr": "Traceback (...略...)ansible.module_utils.connection.ConnectionError: description TEST\r\n                   ^\r\n% Invalid input detected at '^' marker.\r\n\r\ncsr1000v(config)#\n",
  • ログを元にしたコマンド実行の再現イメージ(エラー)
csr1000v(config)#description TEST
                   ^
% Invalid input detected at '^' marker.
ncsr1000v(config)#

なお、これらの動作は、src の代わりに lines で以下のように指定した場合と同じです。このタスクでは親階層を示すコマンドである inteface GigabitEthernet3 含めてすべて lines オプションに指定しているため、正しく動作しません。

- name: config test
  ios_config:
    lines:
      - inteface GigabitEthernet3  # 正しくは parents オプションに指定すべき
      - description TEST
      - ip address 172.16.0.1 255.255.255.0

インデントがあるとコンフィグ階層が正しく解釈される

インデントがあれば、ios_config モジュールはコンフィグの階層を解釈します。解釈したうえで、ネットワーク機器側で設定済みかどうかを確認します。

例えば、もし

interface GigabitEthernet3
 no ip address

という状態のネットワーク機器(先ほどと同じ)に対して、

interface GigabitEthernet3
 description TEST
 ip address 172.16.0.1 255.255.255.0

というインデントがあるコマンドファイルを src に指定して実行すると、description TESTip address 172.16.0.1 255.255.255.0 コマンドが、interface GigabitEthernet3 配下である、という階層に解釈されます。そのため、interface GigabitEthernet3 は、親階層のコマンドとして必ず実行されます。

そして、差分コンフィグ生成の際、ネットワーク機器側の interface GigabitEthernet3description TESTip address 172.16.0.1 255.255.255.0 コマンドがないため、(コンフィグ階層を保ったまま)実行する必要があると判断されます。

その結果、以下の 3行がすべて実行されます。

interface GigabitEthernet3
 description TEST
 ip address 172.16.0.1 255.255.255.0

f:id:akira6592:20181217120807p:plain
インデントがあると階層を解釈して比較

  • コマンド実行イメージ(正常)
csr1000v(config)#interface GigabitEthernet3
csr1000v(config-if)#description TEST
csr1000v(config-if)#ip address 172.16.0.1 255.255.255.0

なお、これらの動作は、src の代わりに linesparents で以下のように指定した場合と同じです。このタスクでは親階層を示すコマンドである inteface GigabitEthernet3lines ではなくparents に指定しているため、正しく動作します。

- name: config test
  ios_config:
    parents:
      - inteface GigabitEthernet3     # 正しい指定
    lines:
      - description TEST
      - ip address 172.16.0.1 255.255.255.0


■ まとめ(再掲)

  • ios_config モジュールの src で指定するコマンドファイルは階層を示すためにインデントが必要
  • インデントの形式はネットワーク機器側の show run で表示される形式と同じ
  • インデントがない場合、すべてグローバルコンフィギュレーションモードで実行しようとしてしまう
  • インデントがある場合、コンフィグの階層を解釈したうえで正しく実行する

[Bolt] エージェントレスなインフラ自動化ツール「Puppet Bolt」かんたんチュートリアル

■ はじめに

Puppet Bolt はエージェントレスなインフラ自動化ツール

Ansible や Chef などとよく比較される Puppet という構成管理ツールがあります。 Puppet といえばエージェント型のイメージがありますが、2018年10月にエージェントレス型の別ツール「Puppet Bolt(以下、Bolt)1.0」がリリースされました。

puppet.com

thinkit.co.jp

Bolt は、サーバーに対してパッケージのインストールやサービスの起動などの設定作業を自動化できます。エージェントレスという点では、Ansible に近いかも知れません。なお、ネットワーク機器に対応しているかどうかは調査できていません。

インストールは Linux にも Windows にもでき、管理対象としても Linux(SSH接続) にも Windows(WinRM接続) に対応していうようです。

この記事では、私が試したインストール方法や簡単な使い方や、補足情報をチュートリアルとしてご紹介します。

主な内容

  • インストール
  • 単純なリモートコマンドの実行(bolt command run
  • スクリプトを指定して実行する(bolt script run
  • 「タスク」を指定して実行する(bolt task run
  • 「プラン」を指定して実行する(bolt plan run

環境

  • Bolt 1.6.0
  • Bolt インストール先: CentOS 7.5.1804
  • 管理対象: CentOS 7.5.1804


■ 用語の整理

チュートリアルに入る前に、今後出てくる用語の整理です。 少し無理やりなところもありますが、Bolt の用語と Ansible の用語を対応させてみました。

Bolt Ansibe 説明
inventory invnetory 接続先のホスト情報を記載するもの
command ansible コマンドで shellcommand モジュールを利用する アドホックなコマンド実行
task module 特定の機能を持つもの
plan Playbook 一連処理、定義を記述したもの




■ インストール

各種パッケージマネージャーを利用してインストールします。

RHEL の場合

RHEL 6

sudo rpm -Uvh https://yum.puppet.com/puppet6/puppet6-release-el-6.noarch.rpm
sudo yum install puppet-bolt

RHEL 7

今回はこちらの方法を利用しました。

sudo rpm -Uvh https://yum.puppet.com/puppet6/puppet6-release-el-7.noarch.rpm
sudo yum install puppet-bolt

詳細は「Install Bolt on RHEL or SLES」を参照してください。

Windows の場合

Bolt は Windows にも対応していて、MSI や Chocolatey でインストールできます。 詳細は「install Bolt on Windows」を参照してください。

その他

その他、macOS や他の方法のインストールは「Installing Bolt」を参照してください。

他のプラットフォーム向けのインストール手順は以下のページに記載されています。 https://puppet.com/docs/bolt/1.x/bolt_installing.html

インストールできたことを確認するため、バージョンを表示してみます。

$ bolt --version
1.6.0

今回は 1.6.0 というバージョンがインストールされました。

bolt help を実行すると、ヘルプが表示されます。

$ bolt help
Usage: bolt <subcommand> <action> [options]

Available subcommands:
  bolt command run <command>       Run a command remotely
  bolt file upload <src> <dest>    Upload a local file
  bolt script run <script>         Upload a local script and run it remotely
  bolt task show                   Show list of available tasks
  bolt task show <task>            Show documentation for task
  bolt task run <task> [params]    Run a Puppet task
  bolt plan show                   Show list of available plans
  bolt plan show <plan>            Show details for plan
  bolt plan run <plan> [params]    Run a Puppet task plan
  bolt apply <manifest>            Apply Puppet manifest code
  bolt puppetfile install          Install modules from a Puppetfile into a Boltdir

Run `bolt <subcommand> --help` to view specific examples.

where [options] are:
    -n, --nodes NODES                Identifies the nodes to target.
                                     Enter a comma-separated list of node URIs or group names.
                                     Or read a node list from an input file '@<file>' or stdin '-'.
                                     Example: --nodes localhost,node_group,ssh://nix.com:23,winrm://windows.puppet.com
                                     URI format is [protocol://]host[:port]
                                     SSH is the default protocol; may be ssh, winrm, pcp, local, docker, remote
                                     For Windows nodes, specify the winrm:// protocol if it has not be configured
                                     For SSH, port defaults to `22`
                                     For WinRM, port defaults to `5985` or `5986` based on the --[no-]ssl setting
    -q, --query QUERY                Query PuppetDB to determine the targets
        --noop                       Execute a task that supports it in noop mode
        --description DESCRIPTION    Description to use for the job
        --params PARAMETERS          Parameters to a task or plan as json, a json file '@<file>', or on stdin '-'
Authentication:
    -u, --user USER                  User to authenticate as
    -p, --password [PASSWORD]        Password to authenticate with. Omit the value to prompt for the password.
        --private-key KEY            Private ssh key to authenticate with
        --[no-]host-key-check        Check host keys with SSH
        --[no-]ssl                   Use SSL with WinRM
        --[no-]ssl-verify            Verify remote host SSL certificate with WinRM
Escalation:
        --run-as USER                User to run as using privilege escalation
        --sudo-password [PASSWORD]   Password for privilege escalation. Omit the value to prompt for the password.
Run context:
    -c, --concurrency CONCURRENCY    Maximum number of simultaneous connections (default: 100)
        --compile-concurrency CONCURRENCY
                                     Maximum number of simultaneous manifest block compiles (default: number of cores)
        --modulepath MODULES         List of directories containing modules, separated by ':'
        --boltdir FILEPATH           Specify what Boltdir to load config from (default: autodiscovered from current working dir)
        --configfile FILEPATH        Specify where to load config from (default: ~/.puppetlabs/bolt/bolt.yaml)
        --inventoryfile FILEPATH     Specify where to load inventory from (default: ~/.puppetlabs/bolt/inventory.yaml)
Transports:
        --transport TRANSPORT        Specify a default transport: ssh, winrm, pcp, local, docker, remote
        --connect-timeout TIMEOUT    Connection timeout (defaults vary)
        --[no-]tty                   Request a pseudo TTY on nodes that support it
        --tmpdir DIR                 The directory to upload and execute temporary files on the target
Display:
        --format FORMAT              Output format to use: human or json
        --[no-]color                 Whether to show output in color
    -h, --help                       Display help
        --verbose                    Display verbose logging
        --debug                      Display debug logging
        --trace                      Display error stack traces
        --version                    Display the version




■ 単純なリモートコマンドの実行(bolt command run

リモートホストに対して、一度きりの単純なコマンドを実行してみましょう。ここでは hostname を実行します。

  • 実行コマンド
bolt command run "hostname" -n 172.16.0.10 -u vagrant --no-host-key-check -p

各オプションの意味は以下の通りです。

オプション 説明 今回の指定値
command run リモート実行するコマンドを指定するサブコマンド hostname
-n (--nodes) リモートホスト 172.16.0.10
-u (--user) リモートユーザー名 vagrant
--no-host-key-check SSHホストキーのチェックを無効
-p (--password) リモートユーザーパスワード (指定なし、プロンプトで入力)
  • 実行結果
$ bolt command run "hostname" -n 172.16.0.10 -u vagrant --no-host-key-check -p
Please enter your password: (パスワードを入力)
Started on 172.16.0.10...
Finished on 172.16.0.10:
  STDOUT:
    testsv    ← リモートホストで hostname コマンドを実行した結果
Successful on 1 node: 172.16.0.10
Ran on 1 node in 0.67 seconds

補足

リモートホストを指定する -n(--nodes) とトランスポート

-n 172.16.0.10,172.16.0.11 のようにカンマ区切りにすると複数を対象にできます。また、インベントリファイルによって、ホスト情報や接続方式を管理することもできるようです。 もしWindows を対象に WinRM で接続する場合は 、以下のよ

  • -n winrm://winsv01 のようにスキームでトランスポートオプションを指定する
  • -n winsv01 --transport winrm のように、 --tranport オプションで指定する

-n オプションの詳細はこちら

トランスポートは以下の4つから選択できます。 - ssh (デフォルト) - winrm - local - docker

その他のオプション

上記で利用したオプションの他にも、秘密鍵の指定(--private-key)や、権限昇格の指定(--run-as)などのオプションもあります。 デバッグ表示する --debug も便利です。 すべてのオプションについては bolt help コマンド結果のヘルプを参照してください。

対応する公式ドキュメント


スクリプトを指定して実行する(bolt script run

Bolt は、先程のように短いコマンドの実行だけでなく、予め用意したスクリプトを指定してリモート上で実行することもできます。 ここでは、myscript.sh という簡単なシェルスクリプトを用意して、bolt 経由でリモート上で実行します。

#!/bin/bash
echo "Hello, Bolt! on (`hostname`)"
  • 実行コマンド
bolt script run myscript.sh -n 172.16.0.10 -u vagrant --no-host-key-check -p
  • オプション説明
オプション 説明 今回の指定値
script run リモート実行するスクリプトを指定するサブコマンド myscript.sh

※その他は bolt command run 実行時と同じ

  • 実行結果
$ bolt script run myscript.sh -n 172.16.0.10 -u vagrant --no-host-key-check -p
Please enter your password: (パスワードを入力)
Started on 172.16.0.10...
Finished on 172.16.0.10:
  STDOUT:
    Hello, Bolt! (on testsv)  ← リモートホストで myscript.sh を実行した結果
Successful on 1 node: 172.16.0.10
Ran on 1 node in 0.68 seconds

今回は、シェルスクリプトを指定しましたが、 Python や、PowerShell(リモートが Windows の場合)でもよいようです。

補足

対応する公式ドキュメント




■ 「タスク」を指定して実行する(bolt task run

今度はタスクの実行です。タスクとは特定の機能をも持つ機能単位で、モジュール名::タスク名 のように指定します。タスク名を省略して モジュール名 のように指定した場合は、 init という名前のタスクが呼ばれます。 さらに、実行するタスクにが利用するオプションと値を必要に応じて指定します。 例えば、service::linux name="postfix" action="stop" のようなかたちです。

(どのようなモジュール、タスクがあるのかは調べきれておりません・・。独自追加もできるようです。)

タスク実行その1: サービスの制御

ここでは、リモートホストhttpd サービスを停止するタスクを実行します。

  • 実行コマンド
bolt task run service::linux name="postfix" action="stop" -n 172.16.0.10 -u vagrant --no-host-key-check -p --run-as root
  • オプション説明
オプション 説明 今回の指定値
task run タスクを指定するサブコマンド service::linux
name タスク service::linux で利用するサービス名 posffix
action タスク service::linux で利用するアクション名 stop
--run-as su 先ユーザー root

※その他は bolt command run 実行時と同じ

  • 実行結果
$ bolt task run service::linux name="postfix" action="stop" -n 172.16.0.10 -u vagrant --no-host-key-check -p --run-as root
Please enter your password: (パスワードを入力)
Started on 172.16.0.10...
Finished on 172.16.0.10:
  {
    "status": "postfix stop"
  }
Successful on 1 node: 172.16.0.10
Ran on 1 node in 1.51 seconds

タスク実行その2: パッケージのインストール

今度は、linux にパッケージをインストールしてみます。利用するタスクは package::linux です。

$ bolt task run package::linux action=install name=wget -n 172.16.0.10 -u vagrant --no-host-key-check -p --run-as root
  • オプション説明 | オプション | 説明 | 今回の指定値 | |---|---|--| | task run | タスクを指定するサブコマンド| package::linux | | name | タスク package::linux で利用するサービス名 | install | | action | タスク package::linux で利用するアクション名 | wget |
$ bolt task run package::linux action=install name=wget -n 172.16.0.10 -u vagrant --no-host-key-check -p --run-as root
Please enter your password:
Started on 172.16.0.10...
Finished on 172.16.0.10:
  {
    "status": "wget install"
  }
Successful on 1 node: 172.16.0.10
Ran on 1 node in 1.97 seconds

補足

モジュールやタスクのオプションなどの情報

タスクの一覧は bolt task show コマンド で確認できます。

  • タスク一覧の表示
$ bolt task show
facts                              Gather system facts
facts::bash                        Gather system facts using bash
facts::powershell                  Gather system facts using powershell
facts::ruby                        Gather system facts using ruby and facter
package                            Manage and inspect the state of packages
package::linux                     Manage the state of packages (without a puppet agent)
package::windows                   Manage the state of packages (without a puppet agent)
puppet_agent::install              Install the Puppet agent package
puppet_agent::install_powershell
puppet_agent::install_shell
puppet_agent::version              Get the version of the Puppet agent package installed. Returns nothing if nonepresent.
puppet_agent::version_powershell
puppet_agent::version_shell
puppet_conf                        Inspect puppet agent configuration settings
reboot                             Reboots a machine
reboot::last_boot_time             Gets the last boot time of a Linux or Windows system
service                            Manage and inspect the state of services
service::linux                     Manage the state of services (without a puppet agent)
service::windows                   Manage the state of Windows services (without a puppet agent)

Use `bolt task show <task-name>` to view details and parameters for a specific task.

各モジュール、タスクの情報は bolt task show モジュール名::タスク名 コマンド で確認できます。

  • service の情報
$ bolt task show service

service - Manage and inspect the state of services

USAGE:
bolt task run --nodes <node-name> service action=<value> name=<value> provider=<value>

PARAMETERS:
- action: Enum[start, stop, restart, enable, disable, status]
    The operation (start, stop, restart, enable, disable, status) to perform on the service
- name: String[1]
    The name of the service to operate on.
- provider: Optional[String[1]]
    The provider to use to manage or inspect the service, defaults to the system service manager

MODULE:
built-in module
  • service::linux の情報
$ bolt task show service::linux

service::linux - Manage the state of services (without a puppet agent)

USAGE:
bolt task run --nodes <node-name> service::linux action=<value> name=<value>

PARAMETERS:
- action: Enum[start, stop, restart]
    The operation (start, stop) to perform on the service
- name: String[1]
    The name of the service to operate on.

MODULE:
built-in module

対応する公式ドキュメント




■ 「プラン」を指定して実行する(bolt plan run

最後に、プランの実行を試します。プランとは、コマンドやタスクなどを組み合わせて一連の処理にまとめたようなものです。

ここでは、以下の3つの処理のプランを作成して、実行します。

  • httpd のインストール
  • httpd の起動
  • index.html のコピー

プランの作成

まず、myweb モジュールのプランを配置するためのディレクトリを作成します。

mkdir -p modules/myweb/plans/

続いてプランのファイル(.pp)を作成します。ここでは myweb モジュールの deploy という名前のプランを作成するため、ファイル名を modules/myweb/plans/deploy.pp とします。

plan myweb::deploy (TargetSpec $nodes) {
  run_task(
    "package::linux",
    $nodes,
    name => "httpd",
    action => "install"
  )

  run_task(
    "service::linux",
    $nodes,
    name => "httpd",
    action => "start"
  )

  upload_file(
    "/vagrant/bolt/index.html",
    "/var/www/html/index.html",
    $nodes
  )
}

bolt plan show でプランの定義を確認できます

$ bolt plan show --modulepath ./modules/
aggregate::count
aggregate::nodes
canary
facts
facts::info
myweb::deploy    ← 作成したプラン
puppetdb_fact
reboot

Use `bolt plan show <plan-name>` to view details and parameters for a specific plan.

プランの処理内容

run_task で、package::linux を指定して、httpd をインストールします。続いて、service::linux で http サービスを起動します。 最後にupload_fileでコンテンツファイルをコピーします。

なお、upload_file で指定するパス "/vagrant/bolt/index.html" は、最初、bolt コマンド実行時からの相対パスで指定しましたが、見つからなかったため絶対パスにしました。

index.html の作成

コンテンツとなる簡単な HTML ファイル(index.html)を作成します。

<html>
<body>
<h1> Hello, Bolt!</h1>
</body>
<html>

実行

  • 実行コマンド
bolt plan run test::command nodes=172.16.0.10 --no-host-key-check -u vagrant -p --run-as root  --modulepath ./modules/
  • オプション説明
オプション 説明 今回の指定値
plan run タスクを指定するサブコマンド test::command
nodes プラン test::command で利用するパラメータ(リモートホスト 172.16.0.10
--modulepath モジュールのパス ./modules/
  • 実行結果
$ bolt plan run myweb::deploy nodes=172.16.0.10 --no-host-key-check -u vagrant -p --run-as root  --modulepath ./modules/
Please enter your password: (パスワードを入力)
Starting: plan myweb::deploy
Starting: task package::linux on 172.16.0.10
Finished: task package::linux with 0 failures in 3.99 sec
Starting: task service::linux on 172.16.0.10
Finished: task service::linux with 0 failures in 1.3 sec
Starting: file upload from /vagrant/bolt/index.html to /var/www/html/index.html on 172.16.0.10
Finished: file upload from /vagrant/bolt/index.html to /var/www/html/index.html with 0 failures in 0.93 sec
Finished: plan myweb::deploy in 6.27 sec
Plan completed successfully with no result

f:id:akira6592:20181220115117p:plain
コンテンツ表示確認(URLは環境都合上のもの)

無事に httpd がインストール、起動し、用意したコンテンツが表示されました。

補足

対応する公式ドキュメント




■ まとめ

エージェントレスなインフラ自動化ツール「Puppet Bolt」で、簡単なコマンド実行や、いくつかの処理をまとめたプランの実行を試してみました。 プランの作成に少々戸惑いましたが、コードで柔軟な処理がかけるかも知れません。 実行コマンドも、今回は長めになってしまいましたが、何かしらの定義ファイルを用意しておけば、もっと短くできそうです。

参考

サイト