全力で怠けたい

怠けるために全力を尽くしたいブログ。

EC2 インスタンスのメタデータを取得する ec2-metadatafs コマンドが便利すぎた。

AWS の EC2 インスタンスメタデータを取得する ec2-metadatafs を使ってみたら便利すぎたのでメモ。

ec2-metadatafs ってなに?

ec2-metadatafs は AWS の EC2 インスタンスメタデータを取得できるコマンド。

ec2-metadatafs の特徴は EC2 インスタンスメタデータファイルシステム上にマウントすることによって ls とか cat とか grep とかの CLI ツールと一緒に使うことが容易になること。 また、メタデータファイルシステム上に配置するのでシェルの補完機能を最大限に活用することができるのがとても便利。

さっそく使ってみる。

$ mkdir metadata

$ ec2-metadatafs --tags metadata/
2019/08/12 05:38:35 [INFO] forked child with PID 3459
2019/08/12 05:38:35 [INFO] child process successfully mounted

$ tree metadata/
metadata/
├── dynamic
│   └── instance-identity
│       ├── document
│       ├── pkcs7
│       ├── rsa2048
│       └── signature
├── meta-data
│   ├── ami-id
│   ├── ami-launch-index
│   ├── ami-manifest-path
│   ├── block-device-mapping
│   │   ├── ami
│   │   └── root
│   ├── events
│   ├── hostname
│   ├── iam
│   │   ├── info
│   │   └── security-credentials
│   │       └── ec2-describetag-role
│   ├── identity-credentials
│   ├── instance-action
│   ├── instance-id
│   ├── instance-type
│   ├── local-hostname
│   ├── local-ipv4
│   ├── mac
│   ├── metrics
│   ├── network
│   │   └── interfaces
│   │       └── macs
│   │           └── 06:e5:0c:b8:f7:3e
│   │               ├── device-number
│   │               ├── interface-id
│   │               ├── ipv4-associations
│   │               ├── local-hostname
│   │               ├── local-ipv4s
│   │               ├── mac
│   │               ├── owner-id
│   │               ├── public-hostname
│   │               ├── public-ipv4s
│   │               ├── security-group-ids
│   │               ├── security-groups
│   │               ├── subnet-id
│   │               ├── subnet-ipv4-cidr-block
│   │               ├── vpc-id
│   │               ├── vpc-ipv4-cidr-block
│   │               └── vpc-ipv4-cidr-blocks
│   ├── placement
│   │   └── availability-zone
│   ├── profile
│   ├── public-hostname
│   ├── public-ipv4
│   ├── public-keys
│   │   └── 0
│   │       └── openssh-key
│   ├── reservation-id
│   ├── security-groups
│   └── services
│       ├── domain
│       │   └── amazonaws.com
│       └── partition
└── tags
    ├── Tag1
    └── Tag2

備忘録までにそれぞれのコマンドがやっていることを軽く書いておく。 まず metadata ディレクトリを作っているところ。

$ mkdir metadata

EC2 インスタンスメタデータをマウントするディレクトリを作っている。 メタデータをマウントする場所はどこでもいいと思うけどユーザーが複数いてそれぞれのユーザーがメタデータを取得するのなら、メタデータはどのユーザーからも見える場所にマウントするとよいと思う。

次は ec2-metadatafs コマンドを実行しているところ。

$ ec2-metadatafs --tags metadata/
2019/08/12 05:38:35 [INFO] forked child with PID 3459
2019/08/12 05:38:35 [INFO] child process successfully mounted

ec2-metadatafs コマンドを使って EC2 インスタンスメタデータをさきほど作った metadata にマウントする。それだけ。

あと EC2 インスタンスのタグを取得するために --tags オプションを指定しているがタグは取得しないのなら指定する必要はない。

次は EC2 インスタンスメタデータをマウントした metadata ディレクトリを tree してみたところ。 EC2 インスタンスメタデータファイルシステム上にマウントされているのがわかる。

$ tree metadata/
metadata/
├── dynamic
│   └── instance-identity
│       ├── document
│       ├── pkcs7
│       ├── rsa2048
│       └── signature
└── meta-data
    ├── ami-id

# 長いので省略

ec2-metadatafs を使ってみる

EC2 インスタンスメタデータファイルシステム上にマウントされる、ただそれだけのことなのだが実際に使ってみると想像以上に便利なのでいくつか使用例を書いておく。

試しにインスタンスタイプを取得してみる。

$ cat metadata/meta-data/instance-type
t2.micro

簡単。

もう一つ試しに EC2 インスタンスが動いているアベイラビリティゾーンを取得してみる。

$ cat metadata/meta-data/placement/availability-zone
ap-northeast-1a

すごく簡単。

ec2-metadatafs が便利なところはシェルの補完機能が働くので「あのメタデータどこだっけ?」ってときも雰囲気で入力して補完機能に任せる、みたいな使い方ができること。これが本当に便利。

補完機能を使うのすら面倒なときは find コマンドとかを使えば一切考える必要もない。

$$ find metadata/ -name "*zone*"
metadata/meta-data/placement/availability-zone

繰り返すが便利。

あとタグを取得できるのが地味だけどかなり便利。

$ ls metadata/tags/
Tag1  Tag2

$ cat metadata/tags/Tag1
Tag1Value

EC2 インスタンスメタデータを取得する方法はいろいろある

EC2 インスタンスメタデータを取得する方法は ec2-metadatafs の他にもあるのでメモしておく。 それぞれがメリット・デメリットがあるので、状況によって使い分けるのがいいと思う。

http://169.254.169.254/latest/meta-data/

http://169.254.169.254/latest/meta-data/ にアクセスすると EC2 インスタンスメタデータを取得できる。

$ curl http://169.254.169.254/latest/meta-data/
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
identity-credentials/
instance-action
instance-id
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
public-keys/
reservation-id
security-groups
services/

インスタンスタイプを取得してみる。

$ curl http://169.254.169.254/latest/meta-data/instance-type
t2.micro

難しいことは一切ないが URL を打ち込むのが面倒くさいし URL を打ち間違えたときに 404 が返ってくるのがイラつく。コピペしたくなってくる。「あれ、インスタンスタイプの URL はなんだったっけ?」というときは http://169.254.169.254/latest/meta-data/ にアクセスして結果を眺めて「あ、instance-type だったな」とやる以外にないのが不便。

あと、どうやってもタグを取得できないのが地味に結構不便。

ただ、http://169.254.169.254/latest/meta-data/ の素晴らしいところは EC2 デフォルトの状態でも使えるということ。 あと、スクリプトの中からメタデータを取得したいときみたいなときは事前に知りたいメタデータの URL とかを調べているだろうし動作も確認しているだろうから、URL を打ち込むのが面倒くさいとか URL を打ち間違えたとかはないはず。

ec2-metadata コマンド

ec2-metadata は EC2 インスタンスメタデータを取得するコマンド。

$ ec2-metadata --help
ec2-metadata v0.1.2
Use to retrieve EC2 instance metadata from within a running EC2 instance.
e.g. to retrieve instance id: ec2-metadata -i
         to retrieve ami id: ec2-metadata -a
         to get help: ec2-metadata --help
For more information on Amazon EC2 instance meta-data, refer to the documentation at
http://docs.amazonwebservices.com/AWSEC2/2008-05-05/DeveloperGuide/AESDG-chapter-instancedata.html

Usage: ec2-metadata <option>
Options:
--all                     Show all metadata information for this host (also default).
-a/--ami-id               The AMI ID used to launch this instance
-l/--ami-launch-index     The index of this instance in the reservation (per AMI).
-m/--ami-manifest-path    The manifest path of the AMI with which the instance was launched.
-n/--ancestor-ami-ids     The AMI IDs of any instances that were rebundled to create this AMI.
-b/--block-device-mapping Defines native device names to use when exposing virtual devices.
-i/--instance-id          The ID of this instance
-t/--instance-type        The type of instance to launch. For more information, see Instance Types.
-h/--local-hostname       The local hostname of the instance.
-o/--local-ipv4           Public IP address if launched with direct addressing; private IP address if launched with public addressing.
-k/--kernel-id            The ID of the kernel launched with this instance, if applicable.
-z/--availability-zone    The availability zone in which the instance launched. Same as placement
-c/--product-codes        Product codes associated with this instance.
-p/--public-hostname      The public hostname of the instance.
-v/--public-ipv4          NATted public IP Address
-u/--public-keys          Public keys. Only available if supplied at instance launch time
-r/--ramdisk-id           The ID of the RAM disk launched with this instance, if applicable.
-e/--reservation-id       ID of the reservation.
-s/--security-groups      Names of the security groups the instance is launched in. Only available if supplied at instance launch time
-d/--user-data            User-supplied data.Only available if supplied at instance launch time.

インスタンスタイプを取得してみる。

$ ec2-metadata --instance-type
instance-type: t2.micro

<メタデータの名前>: <メタデータの値> みたいな感じで出力されるので、メタデータの値たとえばインスタンスタイプの t2.micro の部分だけを取得するなら cut とかで切り出す感じ。ちょっと面倒くさい。

$ ec2-metadata --instance-type | cut -d ' ' -f 2
t2.micro

URL を打ち込む必要がないしショートオプションを使うとタイプ量はさらに少なくなるのが便利。 あと AMI が Amazon Linux ならデフォルトで使えるメリットが大きい。

ただ、メタデータの値を cut とかで切り出すのが面倒くさい。 あと、どうやってもタグを取得できないのが地味に結構不便。

ec2-metadatafs のインストール

ec2-metadatafs コマンドが便利な点と使い方の雰囲気は書いたのでインストール方法も書いておく。

といっても、curlhttps://github.com/jszwedko/ec2-metadatafs/releases/download/<バージョン>/linux_amd64 からダウンロードして実行パーミッションを付けてパスが通ったところに置くだけ。

$ curl -sL https://github.com/jszwedko/ec2-metadatafs/releases/download/1.0.0/linux_amd64 > ec2-metadatafs
$ chmod +x ec2-metadatafs
$ sudo mv ec2-metadatafs /usr/bin/

ec2-metadatafs は EC2 メタデータファイルシステム上にマウントするために FUSE に依存しているので FUSE をインストールしておく。

 $ sudo yum install -y fuse

あとタグを取得するときは IAM ロールが必要になるのでつけておく。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [ "ec2:DescribeTags"],
      "Resource": ["*"]
    }
  ]
}

IAM ロールを設定したくないときは ec2-metadatafs コマンドを使うときに --aws-access-key-id オプションとか --aws-secret-access-key とかを指定してあげてもいい。

ec2-metadatafs を使い終わったら

ec2-metadatafs を使い終わったらというか「あんまり便利じゃないしやっぱ使わなくていいや」というお気持ちになったときは EC2 インスタンスメタデータをアンマウントしておく。

$ fusermount -u metadata

これだけ。

AWS CLI で意図していないプロファイルを使ってしまうのを防止するコマンドを書いたメモ。

AWS CLI で意図していないプロファイルを使ってしまうのを防止するコマンドを書いたメモ。

動機

AWS CLI はプロファイルを --profile オプションや AWS_DEFAULT_PROFILE 環境変数で指定できるようになっていて AWS アカウントや IAM ユーザーが複数あってもあまり手間なくいろいろな操作を行うことができる反面、使うプロファイルを間違うといとも簡単に悲惨なことが起こり得る。

日常の作業の多くは AWS アカウント向け / 操作対象の環境向けのスクリプトを書くなりして操作の入り口から分けてしまうなどの対策を取ればかなりの事故は減らせるが、ちょっとしたお試しとか突発的な作業をするときはどうしても生の aws コマンドを触ってしまいがちでどうしても事故が発生しやすくなってしまう。

そこで、AWS CLI を使うときに Githubリポジトリを消すときの Please type in the name of the repository to confirm. みたいに処理対象を入力させるシステムがあったら事故が減らせるのでは? と思った。

↓こういうやつ。

f:id:ebc_2in2crc:20190728165659p:plain:w400
Githubリポジトリを消すときのアレ

ラップコマンドの実行イメージ

実行イメージはこんな感じ。

AWS CLI のラップコマンド staging-hoge-aws を実行すると対象のアプリケーションと対象の環境を聞いてくる。この実行イメージは hoge が対象のアプリケーションで staging が環境。 staging-hoge-aws コマンドはコマンド内にハードコーディングされたアプリケーションおよび環境とユーザーが入力するアプリケーションおよび環境が 一致するときだけ実際の aws コマンドを実行する。この実行イメージは aws s3 ls が実際のコマンド。

$ staging-hoge-aws s3 ls
Enter target application name: hoge
Enter target environment name: staging

2019-07-27 11:13:21 xxx-bucket
2019-07-27 11:13:21 yyy-bucket

staging-hoge-aws コマンドはユーザーが入力するアプリケーションか環境が間違っているとメッセージを表示して実際のコマンドは実行しない。

$ staging-hoge-aws s3 ls
Enter target application name: fuga
Invalid application name: fuga

このラップコマンドはアプリケーションと環境の組み合わせ分を作っておく。 たとえば hoge アプリケーションは本番環境とステージング環境があるなら production-hoge-awsstaging-hoge-aws を作っておく。

本番環境用のラップコマンドはアプリケーションと環境の確認に加えて「本番環境をいじろうとしてるけど本当にいいの?」的なことを聞いてきてユーザーが yes を入力したときだけ実際の aws コマンドを実行する。ユーザーが yes 以外を入力したときは実際の aws コマンドは実行しない。

$ production-hoge-aws s3 ls
Enter target application name: hoge
Enter target environment name: production
You will make changes to the production environment!!!!!
Are you sure you want to continue (yes/no)?: yes

# ユーザーが yes を入力したときだけ実際の aws コマンドが実行される
2019-07-27 11:13:21 xxx-bucket
2019-07-27 11:13:21 yyy-bucket

ラップコマンドの中身

staging-hoge-aws はこんな感じになっている。 単純に対象のアプリケーションと環境がハードコーディングしてあってユーザーの入力と一致するときに実際の aws コマンドを実行するだけ。

新しいアプリケーションあるいは環境用のコマンドをつくるときは TARGET_APPTARGET_ENV だけ書き換える感じ。

#!/bin/bash

TARGET_APP=hoge
TARGET_ENV=staging
PROFILE=${TARGET_ENV}-${TARGET_APP}

echo -n 'Enter target application name: '
read APP_NAME
if [ "${APP_NAME}" != "${TARGET_APP}" ]; then
    echo "Invalid application name: ${APP_NAME}"
    exit 1
fi

echo -n 'Enter target environment name: '
read ENV_NAME
if [ "${ENV_NAME}" != "${TARGET_ENV}" ]; then
    echo "Invalid environment name: ${ENV_NAME}"
    exit 1
fi

if [ "${ENV_NAME}" == "production" ]; then
    echo "You will make changes to the production environment!!!!!"
    echo -n 'Are you sure you want to continue (yes/no)?: '
    read CONFIRM
    if [ "${CONFIRM}" != "yes" ]; then
        exit 1
    fi
fi
echo ''

aws --profile $PROFILE "$@"

プロファイル

config ファイル

config ファイルはこんな感じにしておく。

[profile staging-hoge]
output = json
region = ap-northeast-1

[profile production-hoge]
output = json
region = ap-northeast-1

credentials ファイル

credentials ファイルはこんな感じにしておく。

[staging-hoge]
aws_access_key_id = <ステージング環境のアクセスキー>
aws_secret_access_key = <ステージング環境のシークレットキー>

[production-hoge]
aws_access_key_id = <本番環境のアクセスキー>
aws_secret_access_key = <本番環境のシークレットキー>

参考にしたサイト

togetter.com

本番環境とステージング環境を取り違える事故を減らしたくて Twitter で聞いてみたら本職のインフラエンジニアから DevOps やってる人まで珠玉の知見を教えてもらいまくったのを参考にした。感謝しかない……っ!!

ipa ファイルから CFBundleVersion を確認するワンライナーのメモ。

ipa ファイルから CFBundleVersion を確認するワンライナーのメモ。

したいこと

ipa ファイル (iOS のアプリ) しかない状態で CFBundleVersion を確認したい。 中間ファイルとかは作りたくない。

確認する方法

CFBundleVersion は ipa ファイルのなかの Payload/<アプリ>.app/Info.plist に記述されているので、それを確認すればよい。 Info.plist の中身はバイナリーだけど plutil で XML なり JSON なりに変換できるので簡単に読める。

個人的には XML よりも JSON のほうが扱いやすいので Info.plist の中身は plutil で JSON に変換して jq で CFBundleVersion を抜き出してる。

実際の確認

unzip -p <IPA ファイルのパス> Payload/<アプリ>.app/Info.plist | plutil -convert json -o - -- - | jq -r .CFBundleVersion でいける。

hoge.app というアプリの CFBundleVersion を確認する例。

$ unzip -p hoge.ipa Payload/hoge.app/Info.plist | plutil -convert json -o - -- - | jq -r .CFBundleVersion
2.1.14

以上。

"草 API サービス" Pixela のブリーダーセットをいただきました!

少し前に Pixela の Go クライアントを 作りました

作っているときは 100% 自分のために作っていたのですが、

blog.a-know.me

Pixela を開発・運営されている a-know さんから「"Pixela( https://pixe.la/ )の活用記事とかライブラリを書いてくれた方" 限定のノベルティグッズを差し上げます!」と連絡いただいたのでありがたく! いただきましたーっ!

f:id:ebc_2in2crc:20190716135833j:plain

下が通常のステッカーで上が Pixela ブリーダーズ限定のステッカーなんですが、サービス同様カッコいいデザイン♪ 缶バッジとあわせてどこに貼ろうか悩んでますw

a-know さん、ありがとうございました!

pixela-client-go が v1.1.0 にバージョンアップしました。

pixela-client-go が v1.1.0 にバージョンアップしました。

github.com

v1.1.0 アップデート内容

optionalData プロパティに対応

日付ごとの要素 Pixel の optionalData プロパティに対応しました。 optionalData プロパティ の使い方に関しては 公式ブログ にとても詳しく書かれているので参照してください。

publishOptionalData プロパティに対応

Graph の publishOptionalData プロパティに対応しました。 publishOptionalData プロパティ の使い方に関しては 公式ブログ にとても詳しく書かれているので参照してください。

現場からは以上です。

Pixela の Go クライアントを作りました

Pixela の Go クライアントを作りました。

github.com

書くこと

  • Pixela とは?
  • 使い方
  • インストール
  • 動機
  • まとめ

Pixela とは?

任意の数値を登録してアレのあれっぽくグラフを作れるクールな API サービスです。

詳しくは↓

pixe.la blog.a-know.me

使い方

package main

import (
    "log"
 
    "github.com/ebc-2in2crc/pixela-client-go"
)

func main() {
    client := pixela.NewClient("YOUR_NAME", "YOUR_TOKEN")

    // 新しいユーザーを作る
    result, err := client.CreateUser(true, true, "")
    if err != nil {
        log.Fatal(err)
    }
    if result.IsSuccess == false {
        log.Fatal(result.Message)
    }

    // 新しいグラフを作る
    result, err = client.Graph("graph-id").Create(
        "graph-name",
        "commit",
        pixela.TypeInt,
        pixela.ColorShibafu,
        "Asia/Tokyo",
        pixela.SelfSufficientNone,
        false,
                 false,
    )
    if err != nil {
        log.Fatal(err)
    }
    if result.IsSuccess == false {
        log.Fatal(result.Message)
    }

    // ピクセルに値を記録する
    result, err = client.Pixel("graph-id").Create("20180915", "5", "")
    if err != nil {
        log.Fatal(err)
    }
    if result.IsSuccess == false {
        log.Fatal(result.Message)
    }

    // ピクセルの値をインクリメントする
    result, err = client.Pixel("graph-id").Increment()
    if err != nil {
        log.Fatal(err)
    }
    if result.IsSuccess == false {
        log.Fatal(result.Message)
    }

    // 新しい webhook を作る
    webhook, err := client.Webhook().Create("graph-id", pixela.SelfSufficientIncrement)
    if err != nil {
        log.Fatal(err)
    }
    if webhook.IsSuccess == false {
        log.Fatal(webhook.Message)
    }

    // webhook を呼び出す
    result, err = client.Webhook().Invoke(webhook.WebhookHash)
    if err != nil {
        log.Fatal(err)
    }
    if result.IsSuccess == false {
        log.Fatal(result.Message)
    }
}

一応すべての API をサポートしています。

と書いておきながら PixeloptionalData プロパティと GraphpublishOptionalData プロパティは自分が使っていないせいか今のところサポートしていません。 対応しました。

インストール

$ go get -u github.com/ebc-2in2crc/pixela-client-go

動機

Pixela はシンプルでいろんなことに応用できるとても便利な API サービスです。 最近は Go のコードを書く機会があったので Go から Pixela を使いたいと思って Go クライアントを探したのですが、なんとなく車輪を再発明してみたくなったので作りました。

まとめ

バグとかあると思いますし「こーしたら使いやすくなる」とかあったらイシュー作ったり声かけてもらえると嬉しいです。

ということで Pixela の Go クライアントの紹介でした。

AWS の CloudWatch ロググループの保持期限を一括で設定するメモ。

AWS の CloudWatch ロググループの保持期限を一括で設定するメモ。 コンソールからたくさんのロググループの保持期限を手で変更していくのが面倒くさかったので AWS CLI で一括設定。

やったこと

aws logs describe-log-groups コマンドで全ロググループを取得できるので取得したロググループに対して aws logs put-retention-policy コマンドで保持期限を設定するだけ。 たとえば以下のコマンドでロググループの保持期限を30日に一括設定できる。

$ aws logs describe-log-groups |
    grep 'logGroupName' |
    sed -e 's/.*logGroupName": "//' -e 's/",.*//' |
    xargs -n1 -I{} aws logs put-retention-policy --log-group-name {} --retention-in-days 30

jq を使うと上のコマンドと同じことをもう少し簡潔に書ける。

$ aws logs describe-log-groups |
    jq -r '.logGroups[].logGroupName' |
    xargs -n1 -I{} aws logs put-retention-policy --log-group-name {} --retention-in-days 30

AWS CLI 便利。

参考ページ

put-retention-policy — AWS CLI 1.16.193 Command Reference