vague memory

うろ覚えを無くしていこうともがき苦しむ人の備忘録

AWS CDK [aws-lambda] log_retention設定

AWS CDK (AWS Cloud Development Kit) に触れた際の備忘録
python 成分多め

[aws-lambda] log_retention 設定用のLambda Function が作成される

設定は数値ではなく、定数を使う必要がある。(例: 3 ではなく、 THREE_DAYS 等)

myFunction という Lambda Function に log_retention を設定した場合、

        fn = aws_lambda.Function(self, 'myFunction',
          code=aws_lambda.Code.asset('lambda'),
          log_retention=aws_logs.RetentionDays.ONE_WEEK,
        );

LogRetention 設定用の LambdaFunction / IAM Role / Log Group が作成される。

Logical ID PhysicalID Type
LogRetention〜 myFunction-LogRetention〜 AWS::Lambda::Function
LogRetention〜ServiceRole〜 myFunction-LogRetention〜 AWS::IAM::Role
LogRetention〜ServiceRoleDefaultPolicy〜 myFun-LogR-〜 AWS::IAM::Policy

deploy 時に LogRetention 設定用の LambdaFunction が実行され、 その LogGroup が作成される。

  • CloudWatch Log Groups
    • /aws/lambda/myFunction-LogRetention〜
      • LogRetention = 1 day

AWS CDK [aws-events] 定期cron式

AWS CDK (AWS Cloud Development Kit) に触れた際の備忘録
python 成分多め

[aws-events] 定期cron式で指定するのは day/week_day のいずれか

時間 曜日 意味
0/15 * * * ? * 15 分ごとに実行
        rule = aws_events.Rule(
            self, "Rule",
            schedule=aws_events.Schedule.cron(
                minute='0/5',
                hour='*',
                day='*',
                month='*',
                #week_day='?',
                year='*'),
            enabled=False,
        )

両方指定は不可

dayとweek_dayの両方指定すると下記エラーとなる。

jsii.errors.JSIIError: Cannot supply both 'day' and 'weekDay', use at most one

? の明示指定は不要

day を指定(*)した場合、week_day に ? が入る。

    Type: AWS::Events::Rule
    Properties:
      ScheduleExpression: cron(0/15 * * * ? *)

dayに ? を指定すると、 dayとweek_dayが ? となり、

    Type: AWS::Events::Rule
    Properties:
      ScheduleExpression: cron(0/15 * ? * ? *)

書式不正で CREATE_FAILED となる。

Parameter ScheduleExpression is not valid. (Service: AmazonCloudWatchEvents; Status Code: 400; Error Code: ValidationException;

AWS CLI v2 で alpine glibc 問題に遭遇

alpine で AWS CLI v2 を使おうとしたら地味に嵌り、 結果、有名な(?) glibc 問題だったというオチでした。

結論

公式がイメージを配布しているので、そちらを使いましょう。 (CLI用のentrypointになっているので、必要に応じてオーバーライド)

もしくはサポートしているディストリビューションのイメージへインストールしましょう。

alpineへ不足ライブラリを入れる事でも実現可能ですが、拘り無いなら上記2点で良いのかなと思います。

経緯

alpine へ公式ドキュメント通りにインストール。
一部略してますが、buildのログは以下のような感じに、 Successfully となってはいます。

Step 1/3 : FROM python:3.8-alpine
Step 2/3 : RUN apk --update --no-cache add   curl
Step 3/3 : RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && unzip awscliv2.zip && ./aws/install
Archive:  awscliv2.zip
   creating: aws/
   creating: aws/dist/

・・・略

  inflating: aws/dist/botocore/data/comprehendmedical/2018-10-30/paginators-1.json
./aws/install: line 78: /aws/dist/aws: not found
You can now run: /usr/local/bin/aws --version
Successfully built xxxxxxxxxxxxx

コンテナの中身を見てみると、ファイル自体は存在し、パスも通っているが、実行時に not found となる。

/ # which aws
/usr/local/bin/aws
/ # aws --version
sh: aws: not found
/ # /usr/local/bin/aws
sh: /usr/local/bin/aws: not found
/ # ls -l /usr/local/bin/aws
lrwxrwxrwx    1 root     root            37 May  3 05:44 /usr/local/bin/aws -> /usr/local/aws-cli/v2/current/bin/aws
/ # ls -l /usr/local/aws-cli/v2/current/bin/aws
lrwxrwxrwx    1 root     root            11 May  3 05:44 /usr/local/aws-cli/v2/current/bin/aws -> ../dist/aws
/ # ls -l /usr/local/aws-cli/v2/dist/aws
-rwxr-xr-x    1 root     root       4099736 May  3 05:44 /usr/local/aws-cli/v2/dist/aws
/ #

原因

AWS CLI v2 は glibc を使用するが、alpine は glibc でなく musl-libc を使っているため、単純にインストールするだけでは動作しない。

参考

AWS CLI v2 のリポジトリに同エラーが issue として挙がってます。

Upstart の自動起動無効化

世の中systemdなので無用の長物になりそうですがメモ。
公式に記載がある通りです。

Ubuntuだと14.10以降は override が利用できるようですが、 CentOS6 や Amazon Linux の場合、 提供されている upstart のバージョンが古く、 override は利用できないようです。

公式

With Upstart 0.6.7, to stop Upstart automatically starting a job, you can either:

  • Rename the job configuration file such that it does not end with ".conf".
  • Edit the job configuration file and comment out the "start on" stanza using a leading '#'.

version 1.3 以上では、override ファイルが利用可能

With Upstart 1.3, you can make use of an "override file" and the manual stanza to achieve the same result in a simpler manner [31]:

# echo "manual" >> /etc/init/myjob.override

Note that you could achieve the same effect by doing this:

# echo "manual" >> /etc/init/myjob.conf

公式の Override の説明リンクが切れてますが、 アーカイブがありました。 upstart.at が既に無いようです。

検証

Amazon Linux の ssm-agent が upstart での起動なので、そちらで検証してみます。

  • 環境
$ grep PRETTY_NAME /etc/os-release
PRETTY_NAME="Amazon Linux AMI 2018.03"
$ initctl --version
initctl (upstart 0.6.5)

a) リネーム

効きました。

$ ls -l /etc/init/amazon-ssm-agent.*
-rw-r--r-- 1 root root 760  8月  3  2018 /etc/init/amazon-ssm-agent.conf_

b) コメントアウト

効きました。

$ diff /etc/init/amazon-ssm-agent.conf.org /etc/init/amazon-ssm-agent.conf
17c17
< start on (runlevel [345] and started network)
---
> #start on (runlevel [345] and started network)
$

c) overrideファイルへ "manual"

効きません。

$ ls -l /etc/init/amazon-ssm-agent.*
-rw-r--r-- 1 root root 760  8月  3  2018 /etc/init/amazon-ssm-agent.conf
-rw-r--r-- 1 root root   7  3月 15 01:10 /etc/init/amazon-ssm-agent.override
$ sudo cat /etc/init/amazon-ssm-agent.override
manual
$
  • reboot 後
$ uptime
 01:11:17 up 0 min,  1 user,  load average: 0.08, 0.02, 0.01
$ ps -ef | grep [s]sm-agent
root      2266     1  0 01:11 ?        00:00:00 /usr/bin/amazon-ssm-agent
$

d) confファイルへ "manual"

効きました。

$ diff /etc/init/amazon-ssm-agent.conf.org /etc/init/amazon-ssm-agent.conf
22a23
> manual
$

Ansible include_tasks で become_user

include_tasks を使用した時に読み込んだタスク全てに become 指定するにはどうするのかを調べたメモ。

バージョンにより挙動は変わりそうですが、 ver 2.9.5 時点では以下の挙動のようです。

include_tasks に become は使用できないので、 読み込まれるタスク側で block を使用して全体に適用することになります。

実行例

id と whoami で実行ユーザを確認します。 sub2.yml を include_tasks で読み込みます。

  • main.yml
---
- hosts: all
  tasks:
   ### tasks
   - name: task1
     command: id
     register: result

   - name: task1 result
     debug: var=result.stdout

   - name: task2
     command: whoami
     register: result

   - name: task2 result
     debug: var=result.stdout

   ### import_tasks
   - name: sub1
     import_tasks: sub1.yml
     tags: ["sub1"]
     become: yes

   - name: sub1
     import_tasks: sub1.yml
     tags: ["sub1"]
     become: yes
     become_user: cwagent

   ### include_tasks (loop)
   - name: sub2
     include_tasks: sub2.yml
     with_items:
       - "root"
       - "cwagent"
     loop_control:
       loop_var: loop_item
     tags: ["sub2"]
  • sub1.yml
- name: sub1_task1
  command: id
  register: result

- name: sub1_task1 result
  debug: var=result.stdout

- name: sub1_task2
  command: whoami
  register: result

- name: sub1_task2 result
  debug: var=result.stdout
  • sub2.yml
- block:

  - name: sub2_item
    debug: var=loop_item

  - name: sub2_task1
    command: id
    register: result

  - name: sub2_task1 result
    debug: var=result.stdout

  - name: sub2_task2
    command: whoami
    register: result

  - name: sub2_task2 result
    debug: var=result.stdout

  become: yes
  become_user: "{{ loop_item }}"
  tags: ["sub2"]

実行結果

$ ansible-playbook -i hosts main.yml

PLAY [all] ******************************************************************************

TASK [Gathering Facts] ******************************************************************
ok: [test-instance]

TASK [task1] ****************************************************************************
changed: [test-instance]

TASK [task1 result] *********************************************************************
ok: [test-instance] => {
    "result.stdout": "uid=1000(ec2-user) gid=1000(ec2-user) groups=1000(ec2-user),4(adm),10(wheel),190(systemd-journal)"
}

TASK [task2] ****************************************************************************
changed: [test-instance]

TASK [task2 result] *********************************************************************
ok: [test-instance] => {
    "result.stdout": "ec2-user"
}

TASK [sub1_task1] ***********************************************************************
changed: [test-instance]

TASK [sub1_task1 result] ****************************************************************
ok: [test-instance] => {
    "result.stdout": "uid=0(root) gid=0(root) groups=0(root)"
}

TASK [sub1_task2] ***********************************************************************
changed: [test-instance]

TASK [sub1_task2 result] ****************************************************************
ok: [test-instance] => {
    "result.stdout": "root"
}

TASK [sub1_task1] ***********************************************************************
[WARNING]: Unable to use /home/cwagent/.ansible/tmp as temporary directory, failing back
to system: [Errno 13] Permission denied: '/home/cwagent'
changed: [test-instance]

TASK [sub1_task1 result] ****************************************************************
ok: [test-instance] => {
    "result.stdout": "uid=995(cwagent) gid=993(cwagent) groups=993(cwagent)"
}

TASK [sub1_task2] ***********************************************************************
changed: [test-instance]

TASK [sub1_task2 result] ****************************************************************
ok: [test-instance] => {
    "result.stdout": "cwagent"
}

TASK [sub2] *****************************************************************************
included: /private/tmp/ansible-test/sub2.yml for test-instance
included: /private/tmp/ansible-test/sub2.yml for test-instance

TASK [sub2_item] ************************************************************************
ok: [test-instance] => {
    "loop_item": "root"
}

TASK [sub2_task1] ***********************************************************************
changed: [test-instance]

TASK [sub2_task1 result] ****************************************************************
ok: [test-instance] => {
    "result.stdout": "uid=0(root) gid=0(root) groups=0(root)"
}

TASK [sub2_task2] ***********************************************************************
changed: [test-instance]

TASK [sub2_task2 result] ****************************************************************
ok: [test-instance] => {
    "result.stdout": "root"
}

TASK [sub2_item] ************************************************************************
ok: [test-instance] => {
    "loop_item": "cwagent"
}

TASK [sub2_task1] ***********************************************************************
changed: [test-instance]

TASK [sub2_task1 result] ****************************************************************
ok: [test-instance] => {
    "result.stdout": "uid=995(cwagent) gid=993(cwagent) groups=993(cwagent)"
}

TASK [sub2_task2] ***********************************************************************
changed: [test-instance]

TASK [sub2_task2 result] ****************************************************************
ok: [test-instance] => {
    "result.stdout": "cwagent"
}

PLAY RECAP ******************************************************************************
test-instance              : ok=25   changed=10   unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

import_tasks と include_tasks について

いずれも別ファイルのタスクを読み込む機能ですが、 静的か動的かということで挙動が異なります。 基本的には import 、ループしたい場合は include という感じでしょうか。

list オプションでの確認例

$ ansible-playbook -i hosts main.yml --list-hosts

playbook: main.yml

  play #1 (all): all    TAGS: []
    pattern: ['all']
    hosts (1):
      test-instance
$ ansible-playbook -i hosts main.yml --list-tasks

playbook: main.yml

  play #1 (all): all    TAGS: []
    tasks:
      task1 TAGS: []
      task1 result  TAGS: []
      task2 TAGS: []
      task2 result  TAGS: []
      sub1_task1    TAGS: [sub1]
      sub1_task1 result TAGS: [sub1]
      sub1_task2    TAGS: [sub1]
      sub1_task2 result TAGS: [sub1]
      sub1_task1    TAGS: [sub1]
      sub1_task1 result TAGS: [sub1]
      sub1_task2    TAGS: [sub1]
      sub1_task2 result TAGS: [sub1]
      sub2  TAGS: [sub2]

$ ansible-playbook -i hosts main.yml --list-tags

playbook: main.yml

  play #1 (all): all    TAGS: []
      TASK TAGS: [sub1, sub2]

AWS CloudWatch Dashboard 複製

簡単にコピーしたかったのです。

同一アカウント

マネジメントコンソール上で別名で保存が簡単かと思います。

f:id:htnosm:20190811141000p:plainf:id:htnosm:20190811141008p:plain

別アカウント

CLI を使います。 jq 使います。

# 複製元のAWSアカウントから DashboardBody を取得

$ aws cloudwatch get-dashboard \
--dashboard-name "sample-dashboard" \
--query 'DashboardBody' > sample-dashboard.json


# 複製先のAWSアカウントに対して、取得した JSON でダッシュボード作成

$ aws cloudwatch put-dashboard \
--dashboard-name "sample-dashboard-copy" \
--dashboard-body "$(jq -r . sample-dashboard.json)"

{
    "DashboardValidationMessages": []
}

jq が無い環境では自力で JSON 書式を調整します。
Text Widget で改行使っている等がある場合はその辺りも考慮必要です。 (下記例は考慮していません) 複製してから修正すれば良い訳ですが。

# sed 例
$ aws cloudwatch put-dashboard \
--dashboard-name "sample-dashboard-copy" \
--dashboard-body "$(sed -e 's/\\"/"/g' -e 's/^"//' -e 's/"$//' sample-dashboard.json)"

おまけでワンライナー

$ aws cloudwatch put-dashboard \
--dashboard-name "sample-dashboard-copy" \
--dashboard-body "$(aws cloudwatch get-dashboard \
  --dashboard-name "sample-dashboard" \
  --query 'DashboardBody' | jq -r . )"

dashboard-name を同一名にしてしまうと上書きされるので注意です。

AWS CloudWatch ECSタスク数

普通に公式に記載がありますが、当たり前過ぎて逆に辿り着くのに時間を要したのでメモ。

データが常時報告される何かしらのメトリクス(CPUUtilization等)の SampleCount で、 RUNNING状態のタスク数のみではありますが、簡単に参照できます。 合計(SUM(METRICS()))すればCluster単位でも出せます。

データポイントの数なので、 Period は 1 Minute です。 (保持期間より過去に遡る場合は、延ばした Period 分で除算すれば算出可能)

ECS (Fargate) のタスク数推移を見たかったわけですが、 サードパーティ製監視サービスの統合機能だと、 Running Task だったり DesiredCount だったり、そのものズバリの名前でメトリクスが用意されているので CloudWatchでも同様のメトリクスがあるものと勝手に思い込んでました。