無印吉澤

ソフトウェア開発、運用管理(俗にいう DevOps)、クラウドコンピューティングなどについて、吉澤が調べたり試したことを書いていくブログです。

Ansible 2.4 で import_tasks/include_tasks に tags を付けるときの注意点

f:id:muziyoshiz:20160331232512p:plain:w300

前提:Ansible 2.4 から include は非推奨になった

Ansible 2.4 を使っていたら既に嫌というほど見てると思いますが、include を使うと以下のような警告が出るようになりました。

[DEPRECATION WARNING]: The use of 'include' for tasks has been deprecated. Use 'import_tasks' for static inclusions or 'include_tasks' for dynamic inclusions. This feature will be removed in a future release. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.

Ansible 2.4 から、いままで include が持っていた機能は以下の4つのアクションに分けられました。

Action Operation Reusable content
import_tasks static task
include_tasks dynamic task
import_playbook static playbook
include_playbook dynamic playbook
include (DEPRECATED) both static and dynamic task or playbook

この変更は、静的(static)な読み込みと動的(dynamic)な読み込み、そして読み込む対象(task ファイルか playbook ファイルか)を明確に区別することを目的に行われたようです。

static と dynamic の違い

ここで言う static と dynamic の意味は、公式サイトの Creating Reusable Playbooks によると次の通りです。

  • 静的な読み込み(static import)は、Playbook をパースする段階で、事前に実行される
  • 動的な読み込み(dynamic include)は、タスクを順に処理して、その行に来た段階で実行される

include は基本的には static import を行いますが、状況によって dynamic include を行うなどして動作がわかりづらいため、Ansible 2.8 で廃止予定とのことです。

じゃあ今までに書いた include はどうすればいいのか?というと、個人的な意見ですが、特に理由がなければ import_tasksimport_playbook に書き換えておけばいい と思います。dynamic include にすると文法ミスの事前検知が行われなくなるので、デバッグが面倒になります。

もっと詳しく知りたい方は、以下のページをどうぞ。@heriet さんによる Qiita の記事は、Ansible 2.2 の時点で書かれたものですが、具体例が多く、基本的な考え方の勉強になりました。

本題:import_tasks/include_tasks に tags を付けた場合の動作

ここまでは前提の話で、ここからやっと本題です。

以下のように import_tasks/include_tasks に tags を付けると、その中に含まれるすべてのタスクに同じタグを付けることができます。これは公式の Tags — Ansible Documentation にも書かれた方法です。

- import_tasks: main.yml
  tags: [ tag1 ]
- include_tasks: main.yml
  tags: [ tag1 ]

例えば、main.yml の中身が、

- debug: msg="Task 1"

- debug: msg="Task 2"

の場合、ansible-playbook --tags=tag1 で両方のタスクが実行されます。

しかし、何かの理由で Task 2 だけ実行したくなったとします(Ansible 使ってるとよくありますよね?)。そこで、

- debug: msg="Task 1"

- debug: msg="Task 2"
  tags: [ tag2 ]

と直して、ansible-playbook --tags=tag2 を実行すると、include_tasks を使った場合は Task 2 が実行されません。どうやら、以下のような動作の違いがあるようです。

  • import_tasks に tags を付けた場合は、「main.yml 内のすべてのタスクに追加するタグ」として扱われる
  • include_tasks に tags を付けた場合は、「include_tasks アクション自体に追加するタグ」のように扱われる

ローカルホストで実験

ローカルホストで簡単に実験できるのでやってみましょう。Gist にファイルを用意しました。

Example of import_tasks/include_tasks with tags

これらのファイルを同じディレクトリに置いて、以下のように実行してください。

$ ansible-playbook playbook1.yml --tags=tag1

Ansible 2.4.2.0 で実行した結果は、以下の通りです。include_tasks の場合だけ、tag2 を付けたタスク(Task 2)が無視されているのがわかります。

No. Playbook How to import --tags option Task 0 Task 1 Task 2
1 playbook1.yml include None ok ok ok
2 --tags=tag1 ok ok ok
3 --tags=tag2 - - ok
4 playbook2.yml import_tasks None ok ok ok
5 --tags=tag1 ok ok ok
6 --tags=tag2 - - ok
7 playbook3.yml include_tasks None ok ok ok
8 --tags=tag1 ok ok ok
9 --tags=tag2 - - -

まとめ:結局どうしたらいいのか?

import_tasksinclude_tasks を使った場合で、タグの扱いに微妙な違いがあることがわかりました。

上記の結果を見ると、「include_tasks 使うとタグが無視されることがあるのか。使うのやめよう」と思うかもしれません。しかし、以下のように書かれているのに、main.yml 内に tag1 以外のタグがついているというのは、それはそれで可読性に問題があります。

- import_tasks: main.yml
  tags: [ tag1 ]

そう考えると、結論としては「import_tasks/include_tasks で読み込むタスクにはタグを付けない」というルールを設けるのが良いのではないでしょうか。

ansible コマンドでのログ検索結果を TSV や Markdown 形式に変換する方法

f:id:muziyoshiz:20171211235628p:plain

これは Ansible Advent Calendar 2017 の13日目の記事です。

ansible コマンドでログ検索

ansible コマンドって使ってますか?

Ansible と言えば普段は ansible-playbook コマンドを使うと思うので一応解説しておくと、ansible コマンドは Ansible モジュールを1個だけ実行するコマンドです。-m でモジュール、-a でそのモジュールに渡す引数を指定します。Running Ad Hoc Commands - Ansible Tips and Tricks あたりが詳しいです。

この ansible コマンドで shell モジュール を使うと、複数サーバ上のログを簡単に検索できます。

例えば以下のように、すべてのアプリケーションサーバに対して、一括で ERROR ログ数を検索できます。

$ ansible -i hosts app -m shell -a "cat /var/log/foobar | grep "ERROR" | wc -l"
app1 | SUCCESS | rc=0 >>
177

app2 | SUCCESS | rc=0 >>
84

app3 | SUCCESS | rc=0 >>
37

僕の場合は wc の代わりに head を使って ERROR ログを何件か表示し、サーバごとの傾向の違いを調べることもあります。

$ ansible -i hosts app -m shell -a "cat /var/log/foobar | grep "ERROR" | head -5"

もちろん、普段の運用に必要なログは、特定のサーバやデータベースに集めておくべきです。わかってますよ、わかってるんですが、集めてないログを調べたくなるときはどうしてもあって。そういうときに ansible コマンドで手軽に検索できるのは便利です。

ansible コマンドの結果を TSV/JSON/Markdown/Backlog 形式に変換

ログを調べたら、(それが仕事なら)たいてい他の人に共有したくなるわけですが、この出力は若干使い勝手がよくありません。

Excel や Google Spreadsheet で処理するにも手作業でのコピペが必要になりますし、Wiki にそのまま貼るにも不便です。以前からこの出力がちょっと気になっていました。

そこで Go の勉強がてら、ansible コマンドの結果を他の形式に変換する ansible2tab コマンドを作ってみました。

github.com

先ほど例に挙げた出力をこの ansible2tab に通すと、以下のように変換されます。

$ ansible -i hosts app -m shell -a "cat /var/log/foobar | wc -l" | ansible2tab
app1    177
app2    84
app3    37

デフォルトが TSV なので ansible2tab という名前にしましたが、--format を指定すると JSON などにも変換できます。ちなみに、僕が仕事で使うので Backlog 記法にも対応しました。

$ ansible -i hosts app -m shell -a "cat /var/log/foobar | wc -l" | ansible2tab --format json
{"app1":"177","app2":"84","app3":"37"}
$ ansible -i hosts app -m shell -a "cat /var/log/foobar | wc -l" | ansible2tab --format markdown
|Host|Value|
|---|---|
|app1|177|
|app2|84|
|app3|37|
$ ansible -i hosts app -m shell -a "cat /var/log/foobar | wc -l" | ansible2tab --format backlog
|Host|Value|h
|app1|177|
|app2|84|
|app3|37|

出力が複数行の場合はうまく表になりません。その場合は --format markdown-code--format backlog-code と指定すると、以下のようなコードブロックに変換します。

gist.github.com

できることはこれだけの、地味なツールです。でも、個人的には日常業務で結構こういうことしてるんですよね……。

この ansible2tab コマンドですが、Mac なら brew 経由でインストールできます。

$ brew tap muziyoshiz/ansible2tab
$ brew install ansible2tab

それ以外の環境では リリースページ からダウンロードしてパスを通すか、あるいは Go の開発環境があれば以下のコマンドでインストールできます。

$ go get github.com/muziyoshiz/ansible2tab

まとめ

ansible コマンドは、複数サーバ上のログを手軽に検索するには便利です。今回は、このログ検索した結果を TSV/JSON/Markdown/Backlog 出力する ansible2tab を作りました。Go で実装したおかげで、さくっとクロスプラットフォーム対応できました。

世間に似たようなニーズがどれくらいあるかわからないですが、よかったら使ってみてください。別の形式にも対応して欲しい、JSON はもっと違う形式がいい、などの要望は GitHub の Issue@muziyoshiz までお願いします。

あと、Go 初心者なのでコードがあまりイケてないと思います。そのあたりのツッコミも歓迎です。

今回の参考情報

Go 言語全般

みんなのGo言語【現場で使える実践テクニック】

みんなのGo言語【現場で使える実践テクニック】

Go のコードに詰まってから読み返すと、ヒントになることが書いてある(と気づく)本でした。良い本です。

以下、今回参考にさせて頂いた Web 上の情報です。

バイナリの配布方法

AWS re:Invent 2017 Serverless re:Cap レポート 〜 Lambda, AppSync, Fargate, Cloud9, ML Service

f:id:muziyoshiz:20171120220420p:plain

最近、Serverless 関係の開発力を付けないとなあ……と思っていることもあって、AWS のイベントに参加してきました。

いずれちゃんとしたレポートが出てくると思いますが、個人的に気になった部分のメモを公開しておきます。

Serverless Updates (AWSJ SA 小梁川貴史)

  • AWS Lambda
    • メモリ容量を最大3GBまで設定可能に
    • Go 言語と .NET Core 2.0 のサポートを プリアナウンス
    • CloudTrailに起動イベント(invoke)も記録されるように
    • 関数ごとに同時実行可能数の上限を設定可能に(いままではアカウント全体での上限のみ)
    • コンソールから、その関数に設定したロールでアクセス可能なサービスを確認できるようになった
    • CodeDeploy での段階的デプロイのサポート
    • AWS Serverless Repository
  • Amazon API Gateway
    • いままでは Lambda のVPCアクセスを挟む必要があったが、その必要がなくなった
    • カナリアリリースのサポート(複数バージョンの混在)
    • API Integration(統合API)のタイムアウトのカスタマイズ設定が可能に
    • アクセスログの書式指定が可能に
  • Amazon Cognito
    • 認証機能の強化(ASF)
  • AWS Step Functions
    • re:Invent 前に発表された内容:state machine の update が可能となった

(※もし、スライドが公開されたら「本日ご紹介した update 一覧」のページだけ見れば概要がわかる)

AWS AppSync (AWSJ PS 塚越啓介)

  • DevOps Consultant の方
  • AppSync
    • フルマネージド GraphQL サービス
    • リアルタイム機能とオフライン機能にフィーチャ
    • 現在パブリックプレビュー中
  • AppSync のコンセプト
    • AWS AppSync Client: クライアントライブラリの提供
    • DataSource: Amazon DynamoDB, ElasticSearch, AWS Lambda をサポート
    • Identity: GraphQL Proxy での認証
    • GraphQL Proxy: リクエストのマッピングなど
    • Operation:
    • (※あと2つの特徴はメモできなかった)
  • GraphQL の解説
  • GraphQL Subscription
    • Mutation をトリガーにしたイベントベースモード
  • コンソールのデモ
    • Schema, Data Source の定義など
    • GraphiQL みたいなコンソールを提供してくれる
  • クライアントのデモ
    • ネットワークが繋がらないときは、ローカルストレージにキャッシュされる
    • ネットワークが復帰すると、バックグラウンドで同期される
    • 片方のクライアントからイベントを送ると、subscribeしている他のクライアントにpublishされて、画面が更新される
  • 質問
    • pagination の実装はどれくらい面倒見てくれるのか?
      • 支援の機能はある。詳しくはサンプルコードを参照
      • データベース側への制約はない。DynamoDB 側で next token とか設定すれば、自動的にやってくれる

AWS Fargate (AWSJ SA 大村幸敬)

  • Fargate 概要
    • インスタンス管理不要
    • タスクネイティブAPI
      • タスク → 複数のコンテナを1つにまとめた単位
      • タスクごとに Elastic Network Interface (ENI) が振られる → タスクに直接 IP アドレスを設定できる
      • ECS の場合は、コンテナはホストと通信する構成だった
    • リソースベースの価格
      • CPU と メモリは、50 種類のパターンから選択
      • 秒単位の課金
    • SLA は 99.99%(EC2 と同等レベル)
    • 現在、全世界の Kubernetes 上で動くワークロードの 63% は、AWS 上で動いている
    • Amazon EKS
      • EKS であっても、インフラ部分の管理は必要
      • Fargate の EKS サポートは 2018 年の予定
  • Lambda と Fargate の使い分け
  • デモ
    • マルチAZ、パブリックサブネット/プライベートサブネットの構成
    • タスクごとに CPU、メモリなどを指定
  • Fargate Under the Hood
    • Task Definition を登録。これを使ってタスクを実行
    • Per task ENI で動くので、今まで使っていた VPC でのアクセス制御が可能
    • ALB/NLB との組み合わせが可能
    • visibility がなくなった部分は、CloudWatch でメトリクスを確認するなど
    • Fagate 関連セッション(CON333, CON401 あたりが面白い)

AWS Cloud9 (AWSJ Specialist SA 福井厚)

  • Cloud9
    • クラウドネイティブなIDE
    • EC2 か、SSH 接続可能な Linux サーバにインストールできる
    • Cloud9 からコンソールを実行して、AWS CLI を利用したりできる
    • 複数のダッシュボードを利用して、(アカウントを?)切り替えられる
    • AWS CodeStar と連携
    • 一般的な IDE と同様の操作(例:ショートカットキー、ステップ実行、ブレークポイントなど)
  • Serverless Application Integration
    • リモートの Lambda ファンクションを参照できる
      • Cloud9 をインストールできるリージョンは限られるが、Lambda は任意のリージョンのものを参照できる
    • SAM のテンプレートを自動作成
    • Cloud9 上で Lambda のパラメータのペイロードを変更して、実行できる
  • Collaboration
    • IAM ユーザ間で Cloud9 の環境を共有できる
    • 共有したユーザ間でのチャット、ペアプログラミング(共同編集)ができる
  • AWS CodeStar Integration
    • CodeStar から Cloud9 の環境を構築できる
  • Cloud9 の利用自体は無料
    • EC2 の実行時間と、ストレージ利用量だけが課金対象

ML Services (AWSJ Specialist SA 西谷圭介)

  • 西谷さん、2月末に「サーバーレスアプリケーション開発ガイド」を発売予定
  • AWS の ML サービススタックの Services 層に追加された4つの製品の紹介
  • Amazon Compprehend
    • 自然言語理解サービス
    • 英語とスペイン語に対応
    • キーフレーズの抽出、関連する用語の分類、言語の認識、感情分析(文章のニュアンス)
    • Twitter 等のリアルタイム分析だけでなく、S3 上のファイルに対するバッチ処理も可能
  • Amazon Translate
  • Amazon Rekognition Video
    • 画像認識サービス Amazon Rekognition の動画版
    • 分析結果は1つの JSON で返される
    • JSON にさまざまな分析結果が格納されており、必要に応じて使う
  • Amazon Transcribe
    • 音声をテキストに変換するサービス(文字起こし)
    • プレビューでの対応言語は英語とスペイン語
    • 通常音声と電話音声の両方をサポート
    • タイムスタンプと、その時刻の文字起こしの信頼度
    • ユースケース:コールセンターの音声データの可視化
      • S3 -> Lambda -> Amazon Transcribe -> Amazon Comprehend -> Athena -> QuickSight

感想

Serverless といいつつ、真っ先に連想されそうな Lambda の話はあまりありませんでした。最初のサマリと、あとは Cloud9 の話題が一番 Lumbda に近かったですかね。それでも結構面白かったです。

Fargate は、運用の楽さと AWS サービスとの連携機能が魅力的で、Docker を使うならやっぱり便利そう。Fargate の EKS 対応がリリースされたら、実業務で使ってみたいところです。

GraphQL は以前に少しだけ調べたことがあるんですが、GraphQL Subscription というのが出ているのは知りませんでした。動きが早い分野なので時々見ないと駄目ですね。この辺を読むのがいいでしょうか?

Amazon Linux, RHEL, CentOS での pip のインストール方法の違い

f:id:muziyoshiz:20171120220420p:plain

やらかした話

Amazon Linux は Red Hat Enterprise Linux (RHEL) をベースに開発された Linux ディストリビューションです*1。しかし、古い RHEL 5〜6 をベースにして、その後の開発は分岐しているため、パッケージ管理の方法などには違いがあります。

RHEL や CentOS では、EPEL を使って pip をインストールしても最新版にならず、pip install -U pip (pip install --upgrade pip) を実行する必要があります。

しかし Amazon Linux には独自の yum repository があるため、以下のように yum で最新版にアップデートできます。

$ sudo yum update python27-pip

しかし、それを忘れていて、Amazon Linux 上でつい

$ sudo pip install -U pip

としたところ、/usr/bin/pip が削除されて、新しい pip が /usr/local/bin/pip にインストールされてしまいました。ぐぐってみたら、同様の報告が Stack Overflow にもありました。

stackoverflow.com

「もしかして、これって最新の RHEL や CentOS でも同じことになるんだろうか?」と思って、EC2 上で検証してみました。

検証結果

結果を先に言うと、RHEL や CentOS ではそうなりませんでした。

  • Amazon Linux AMI 2017.09.1

    • 最新の pip が入っており、これ以上アップデートできない(Python 2.7.12, pip 9.0.1)
  • Amazon Linux AMI 2017.03.0

    • Python 2.7.12, pip 6.1.1 がインストールされている
    • pip は alternatives で管理されており、/usr/bin/pip は /etc/alternatives/pip へのシンボリックリンク
    • pip install -U pip を実行すると、/usr/bin/pip は削除され、/usr/local/bin/pip にインストールされる
    • yum update python27-pip でアップデートすれば /usr/bin/pip のままで pip 9.0.1 になる
  • Red Hat Enterprise Linux 7.4 (HVM), SSD Volume Type

    • Python のバージョンは 2.7.5 で、pip はインストールされていない
    • yum install epel-release で EPEL をインストールできず、rpm ファイルをダウンロードしてインストールする必要がある
    • yum install python-pip で pip 8.1.2 がインストールされる
    • インストール先は Amazon Linux と同じ /usr/bin/pip
    • pip install -U pip を実行すると、パスは /usr/bin/pip のままで pip 9.0.1 にアップデートされる
  • CentOS 7 (x86_64) - with Updates HVM

    • Python のバージョンは 2.7.5 で、pip はインストールされていない
    • yum install epel-release で EPEL をインストールできる
    • これ以降は RHEL と同じ

補足:現時点の最新バージョンは Python 2.7.14, pip 9.0.1

まとめ

結論としては、Amazon Linux では pip install -U pip しちゃ駄目ですが、他の Red Hat 系だと特に問題ない(むしろ yum では最新版が入らない)みたいです。Amazon Linux の pip は alternatives で管理されていることが影響しているんでしょうか?

Qiita で同様の症状がいくつか報告されていて、これらはどうも Amazon Linux ではなさそうですが、これ以上は深入りしないでおきます。

この件を Python に詳しい同僚に話したところ、yum が Python 2 で動作する関係で、RHEL や CentOS ではなかなか Python のバージョンを上げられないという事情もあるそうです。

pip は Python 3.4 以降に標準添付されていますが、RHEL や CentOS でその恩恵が受けられるのは当分先になりそうですね……。

おまけ(1):今回の検証に使った AMI

f:id:muziyoshiz:20171120220437p:plain
f:id:muziyoshiz:20171120220448p:plain
f:id:muziyoshiz:20171120221007p:plain

おまけ(2):そもそものきっかけ

今回そもそも何故 Amazon Linux で pip をアップデートしようとしたかというと、こんな経緯でした。

pyenv や virtualenv を使っていれば、今回みたいな目には遭わないと思います。Ansible で使いたいだけだから、と手抜きしたのがよくなかったですね。

Ansible が Python に依存してるせいで、ときどきどうでもいいところでつまづく気がします(今回のは完全に自業自得ですけど)。運用管理ツールを Go で作りたくなる人の気持ちが最近よくわかります……。

*1:AWS Developer Forums: Amazon Linux AMI - what distro is this based on? での Ben@AWS の回答によると、Amazon Linux は RHEL 5.x と、RHEL 6 の一部を元にしている。