継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

『Google Cloud Certified - Associate Cloud Engineer』に合格するためにやったこと

タダです.

勉強の一環で5/27 に「Google Cloud Certified - Associate Cloud Engineer」を受けて合格しました.今回は自分がどんな勉強をしたかを振り返り,今後受験される方の勉強の参考になればと思い整理します.なお,自分は GCP は業務で一部使っている程でシステム基盤として使ったことはないレベルです.

cloud.google.com

試験範囲について

試験範囲については試験のガイドで確認できます.GCP のサービスを使ってアプリケーションのデプロイ,モニタリング,管理周りの知識が問われそうだと理解しました.

Associate Cloud Engineer は、目標となるパフォーマンス指標を確実に達成するため、アプリケーションのデプロイ、複数プロジェクトのオペレーションのモニタリング、エンタープライズ ソリューションの管理を行います。

また、パブリック クラウドとオンプレミス ソリューションの実務経験があり、Google Cloud 上での Google マネージド サービスまたはセルフマネージド サービスを活用するデプロイ済みソリューション管理において、一般的なプラットフォームベースのタスクを実行する Google Cloud Console とコマンドライン インターフェースを使いこなせるようになります。

試験の構成

試験の割合が以下のようになっています.AWS のように分野ごとのどれくらいの出題されるかの比重は載っていませんでしたが,分野ごとのサービス例や観点は詳細に記載があり,登場するサービスをさらうときの勉強に活かしました.

  1. クラウドソリューション環境の設定
  2. クラウドソリューションの計画と構成
  3. クラウドソリューションのデプロイと実装
  4. クラウド ソリューションの正常なオペレーションの確保
  5. アクセスとセキュリティの構成

cloud.google.com

試験勉強で注力したこと

次に,試験に向けて注力して勉強したことをまとめていきます.大きく3つのことをやりました.

1. GCP サービス解説している書籍およびドキュメントの読み込み

何はともあれ GCP のサービスを勉強しようと思って,以前買った『取ろう!GCP Professional Cloud Architect』と『Google Cloud Platform エンタープライズ設計ガイド』を読んできました.2冊ともサービスごとに解説ページが簡潔書かれているため,まずはさらっとサービスを理解するのにいいと思います.

booth.pm

加えて『AWS プロフェッショナルのための Google Cloud』という解説ページで AWS サービスと比較したページが個人的に AWS のサービスとリンクさせながら覚えられてよかったです.また,トップゲートさんの GCP 入門編シリーズも勉強になりました.

cloud.google.com

www.topgate.co.jp

2. 模擬問題を解く

回数制限がなく,無料で模擬問題が用意されています.ここで試験の出題のされ方をなんとなく把握できました.

例題は、試験で出題されるトピックの範囲や問題の難度を表すものではありません。例題の成績は、Cloud Engineer 試験の結果の予測には使用しないでください。

例題への解答に回数制限はありません。

模擬試験に時間制限はありません。

cloud.google.com

3. ドキュメントを読む

模擬問題を解いてわからない問題や書籍を読んでからもう少し深掘りたい内容はドキュメントをみていきました.

cloud.google.com

試験の結果

結果は冒頭に記載のように合格できました.なおスコアは出ず,合否のみが表示されます.

f:id:sadayoshi_tada:20210530154203p:plain

合格以降に数日経過後,証明書が発行されます.

f:id:sadayoshi_tada:20210610114612p:plain

まとめ

Google Cloud Certified - Associate Cloud Engineer」に向けて勉強した内容をおおまかにまとめました.AWS のサービスを GCP で置き換えたときの解説ページはサービスとのリンクをさせる時に役立ちました.同じように受験をする人の参考になれば嬉しいです!

FireLens からログだけ Lambda を使って抽出し,Athena でクエリする

タダです.

ECS に乗っているコンテナのログを FireLens を経由して Kinesis Firehose -> S3 にログを出しました.ただ,下記の JSON 形式でログが保存されているため,Athena でクエリしようとするとしてもログの詳細を見る時辛いなと思っていました.

{
    "container_id": "xxx",
    "container_name": "xxx",
    "ecs_cluster": "xxx",
    "ecs_task_arn": "arn:aws:ecs:xxx:xxx:task/xxx/xxx",
    "ecs_task_definition": "xxx:xxx",
    "log": "xxx"
}

関連記事

sadayoshi-tada.hatenablog.com

そこで,Lambda を使って log の部分だけ抽出して S3 に保存し,そのログを Athena でクエリしていきます.

FireLens から送られてくるログを抽出するためのコード

Lambda は Python 3.8 で書いているのですが,ログを抽出し,S3に保存するところのみの部分を抜粋してます.Firehose から送られてくるデータからログの詳細を抽出して Fireshose に戻し S3 に保存されます.

import json
import base64
from botocore.exceptions import ClientError

def lambda_handler(event, context):  
    log_output = []  
    for record in event['records']:
        payload = json.loads(base64.b64decode(record['data']))
        log_detail = payload["log"]

     output_record = {
        'recordId': record['recordId'],
        'result': 'Ok',
        'data': base64.b64encode(json.dumps(log_detail).encode('utf-8')).decode('utf-8')
    }
    
    try:
        log_output.append(output_record)
    except ClientError as e:
        print(e.response['Error']['Message'])
    return {'records': log_output} 

S3 に保存されたログのイメージ

ファイルを開くと,ログ詳細だけが保存されるようになったのでこれで Athena でいい感じにクエリしていけそうです.

{
    "request_headers": {
        "x-forwarded-for": "xx.xx.xx.xx",
        "x-forwarded-proto": "http",
        "x-forwarded-port": "xxxx",
        "host": "xx.xx.xx.xx:xxxx"
    },
    "remote_addr": "xx.xx.xx.xx",
    "request_uri": "/",
    "request_method": "GET",
    "request_time": "2021/05/20 15:17:02",
    "response_time": "0.0004",
    "status": 200,
    "response_headers": {
        "content-length": "xx",
        "content-type": "application/json"
    }
}

参考情報

https://docs.aws.amazon.com/ja_jp/firehose/latest/dev/data-transformation.html#data-transformation-status-model

Athena のテーブル作り

S3 に保存したファイルは上記のような形式で保存されているので,このログファイルをクエリできるようにテーブル作ります.Athena のテーブルは下記のクエリで作りました.JSON の中で内包されている JSON にもクエリできるよう struct 以降で定義しています.また,ログはYYYY/MM/DDのフォルダ配下に入っているので,Partition Projection を使っています.

CREATE EXTERNAL TABLE IF NOT EXISTS `partition_table`(
  `request_headers` struct<`x-forwarded-for`:string,`x-forwarded-proto`:string,`x-forwarded-port`:string,`host`:string>, 
  `remote_addr` string, 
  `request_uri` string , 
  `request_method` string , 
  `request_time` string , 
  `response_time` string , 
  `status` bigint , 
  `response_headers` struct<`content-length`:string, `content-type`:string>
  )
PARTITIONED BY (
  `dateday` string 
)
ROW FORMAT SERDE 
  'org.openx.data.jsonserde.JsonSerDe'
LOCATION
  's3://バケット名/パス'
TBLPROPERTIES (
  'projection.enabled' = 'true',
  'projection.dateday.type' = 'date',
  'projection.dateday.range' = '2021/05/20,NOW',
  'projection.dateday.format' = 'yyyy/MM/dd',
  'projection.dateday.interval' = '1',
  'projection.dateday.interval.unit' = 'DAYS',
  'storage.location.template' = 's3://バケット名/パス/${dateday}'
);

クエリを投げてみる

作ったテーブルに対してクエリを投げてみます.

SELECT * FROM "xxx"."partition_table" where dateday = '2021/05/20';

こんな感じで返ってきます.

1   {x-forwarded-for=null, x-forwarded-proto=null, x-forwarded-port=null, host=xx.xx.xx.xx:xxxx}    xx.xx.xx.xx /   GET 2021/05/20 09:41:18 0.0005  200 {content-length=xx, content-type=application/json}  2021/05/20
2   {x-forwarded-for=null, x-forwarded-proto=null, x-forwarded-port=null, host=xx.xx.xx.xx:xxxx}    xx.xx.xx.xx /   GET 2021/05/20 11:59:25 0.0004  200 {content-length=xx, content-type=application/json}  2021/05/20
3   {x-forwarded-for=null, x-forwarded-proto=null, x-forwarded-port=null, host=xx.xx.xx.xx:xxxx}    xx.xx.xx.xx /   GET 2021/05/20 09:18:47 0.0004  200 {content-length=xx, content-type=application/json}  2021/05/20

内包されている JSON にクエリしてみます.例えば,request_headerの中にあるhostをクエリしてみます.

SELECT request_headers."host" FROM "xxx"."partition_table" where dateday = '2021/05/20';

これでhostにはいっている IP アドレスだけ返ってきます.これでログから必要な情報をクエリしてきそうなのでログ解析が以前よりは楽になりそうです.

1   xx.xx.xx.xx:xxxx
2   xx.xx.xx.xx:xxxx
3   xx.xx.xx.xx:xxxx

参考情報

https://docs.aws.amazon.com/ja_jp/athena/latest/ug/partition-projection.html

まとめ

FireLens 経由で送られるログを必要な情報だけ Lambda で抽出し,Athena でクエリしやすくしてみました.また,Athena の Partion Projection も以前から気になっていたので使えてよかったです.同様の対応をされている方の参考になれば嬉しいです.

GitHub Actions で特定のワークフロー完了後に動作するワークフローを作る

タダです.

GitHub Actions を使っていてこれまでは開発者からのpushpull_requestのイベントをトリガーに動かしていましたが,特定のワークフロー完了後に別のワークフロー を動かすことをやったので設定をまとめていきます.

概要

以下の2つのワークフロー があって,before-action.ymlが完了後にafter-action.ymlが動作するようにしたいです.

  • before-action.yml : 特定ブランチ削除後にタグを打つ処理
  • after-action.yml : タグを打った後の実行される処理

GitHub Actions の定義

上記のことを実現するための GitHub Actions が下記になります.after-action.ymlのトリガーで push イベントを最初設定していましたが,GitHub Actions の中での push があっても動作しませんでした.変わりにworkflow_runを使っています.workflowsでトリガーするワークフロー名,branchesでトリガーのワークフローが実行されるブランチ,typesでワークフロー のステータスが完了になったら動作するよう設定しました.

before-action.yml

name: Before Actions

on:
  delete

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: 'Checkout'
        uses: actions/checkout@v2
      - name: git tag
        run: |
          git tag xxx
          git push origin xxx
        if: startsWith(github.event.ref, 'hoge')

after-action.yml

name: After Actions

on:
  workflow_run:
    workflows: 
      - Before Actions
    branches: main
    types:
      - "completed"

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: 'Checkout'
        uses: actions/checkout@v2
      - name: print
        run: echo "OK"

参考情報

docs.github.com

実行結果

実行結果を確認してみたところ期待通りに動作したことを確認しました.

f:id:sadayoshi_tada:20210518203100p:plain

まとめ

別のワークフロー の結果をトリガーに動作するワークフローを作ったので備忘録としてまとめました.同じような目的の方の参考になれば嬉しいです.

S3 に保存した拡張子を取り除いた html コンテンツが閲覧ができない事象の対処

タダです.

CloudFront と S3 の構成で静的ファイルの配信をすることになり,継続的なコンテンツの更新があるし GitHub Actions でコンテンツをアップロードするようにしました.その際に,静的ファイルの中で html ファイルを拡張子取り除いてアップロードしたものがいくつかあったのですが,アクセスしてみたら拡張子を取り除いたファイルは閲覧できずダウンロードされてしまいました.この記事でこの事象の対応をまとめていきます.

ダウンロードされたファイルの状況

ダウンロードされたファイルを見て見ると content-typebinary/octet-stream になっていました.本来 text/htmlcontent-type で閲覧したいので,アップロードする時に意図したcontent-type にします.

curl -I https://example.com/hoge
HTTP/2 200
content-type: binary/octet-stream

GitHub Actions の処理

該当の処理の部分だけ抜粋したのが下記の GitHub Actions です.拡張子を取り除いたファイルとその他の除外したいファイル以外を s3 sync でアップロードし,拡張子を取り除いたファイルは aws s3 cp --content-type text/html を指定してアップロードします.

- name: Remove html file extension
   run: |
     mv hoge.html hoge
- name: Upload contents to S3
   run: |
      aws s3 sync . ${{ secrets.S3_BUCKET }}  --exclude '*.git*' --exclude "README.md" --exclude "hoge" 
      aws s3 cp --content-type text/html hoge ${{ secrets.S3_BUCKET }}

関連情報

docs.aws.amazon.com

ダウンロードされたファイルに再度アクセスした結果

再度アクセスしてみると今度は text/html になりレスポンスが返ってきて意図したコンテンツの閲覧ができました.

curl -I https://example.com/hoge
HTTP/2 200
content-type: text/html

まとめ

GitHub Actions 経由でアップロードしたファイルを閲覧した時に静的ページではなくコンテンツのダウンロードが発生したのでその対処をまとめました.同じ事態になった際の参考になれば嬉しいです.

WordPress API を API Gateway の HTTP 統合使ってアクセスする

タダです.

業務で AWS 内から外部の WordPress のコンテンツを API 経由で取得しキャッシュもせたいというモチベーションから API Gateway の HTTP 統合を使う機会があったので,この記事で設定を備忘録として記録してきます.

API Gateway の設定

WordPress API の呼び出しは API Gatewayの HTTP 統合を使ってきます.Endpoint URL に WordPoress API のURL を指定します.取得したいコンテンツは per_page という単位で取得したいのですが,per_page の値が固定の場合は Endpoint URL に指定して完了です.per_page の値が可変にしたい場合はクエリ文字列の設定が必要になります.

f:id:sadayoshi_tada:20210512234531p:plain

参考情報

docs.aws.amazon.com

developer.wordpress.org

キャッシュ設定

API Gateway のキャッシュ設定はデプロイのステージで設定します.画像のはキャッシュメモリサイズを 0.5GB に指定した場合です.

f:id:sadayoshi_tada:20210512235923p:plain

なお,キャッシュのサイズに応じて時間料金の課金が発生します.

キャッシュメモリサイズ(GB) 時間あたりの料金
0.5 $0.02
1.6 $0.038
6.1 $0.20
13.5 $0.25
28.4 $0.50
58.2 $1.00
118.0 $1.90
237.0 $3.80

参考情報

docs.aws.amazon.com

まとめ

小ネタですが,AWS 外の API の呼び出し仲介として API Gateway を使ったのは初めてだったのでこんな使い方ができるのかと新鮮な気持ちでした.普段あまり触らないサービスを触ることで当然新しい刺激を得ることができました.