まっしろけっけ

めもてきなやーつ

Golang で ImageMagick を使わずに画像をいじる②

はじめに

shiro-16.hatenablog.com

前回は上記の記事で変換/切り抜き/合成あたりをやりました。
今回は画像のリサイズを行いたいと思います。

画像のリサイズ

まずは画像の縦横のサイズを半分にしてみます。

package main

import (
	"flag"
	"fmt"
	"image"
	"image/jpeg"
	"os"
	"golang.org/x/image/draw"
)

func main() {
	flag.Parse()
	args := flag.Args()

	f, err := os.Open(args[0])
	if err != nil {
		fmt.Println("open:", err)
		return
	}
	defer f.Close()

	img, _, err := image.Decode(f)
	if err != nil {
		fmt.Println("decode:", err)
		return
	}

	fso, err := os.Create(args[1])
	if err != nil {
		fmt.Println("create:", err)
		return
	}
	defer fso.Close()

	rct := img.Bounds()
	
	dst := image.NewRGBA(image.Rect(0, 0, rct.Dx() / 2, rct.Dy() / 2)) // ここで 1/2 のサイズ指定をしている
	draw.CatmullRom.Scale(dst, dst.Bounds(), img, rct, draw.Over, nil)

	jpeg.Encode(fso, dst, &jpeg.Options{Quality: 100})
}

元の画像が 150x150 なので 1/2 することで 75x75 の画像が出来上がります。
それが下記の画像

元画像 切り取り後
f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200605113615j:plain

今回は width と height 両方を 1/2 しましたが image.Rect(0, 0, rct.Dx() / 2, rct.Dy() / 2) の部分を image.Rect(0, 0, 100, 50) などに変更すると下記のような画像が出来上がります

元画像 切り取り後
f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200605114055j:plain

アスペクト比を無視して縮小が行われるので潰れた画像が出来上がってしまう。

そこでアスペクト比を維持したまま長辺のサイズを指定するというのを実現するのが下記のコード

// 上記の defer fso.Close() まで略
	arg, _ := strconv.ParseFloat(args[2], 64)

	rct := img.Bounds()

	var w, h int
	if rct.Dx() > rct.Dy() { // 横長画像
		m := arg / float64(rct.Dx())
		w = int(arg)
		h = int(float64(rct.Dy()) * m)
	} else { // 縦長画像
		m := arg / float64(rct.Dy())
		w = int(float64(rct.Dx()) * m)
		h = int(arg)
	}

	dst := image.NewRGBA(image.Rect(0, 0, w, h))
	draw.CatmullRom.Scale(dst, dst.Bounds(), img, rct, draw.Over, nil)

	jpeg.Encode(fso, dst, &jpeg.Options{Quality: 100})
}

わかりやすいように横長の画像を使用

150 × 84 を使用して下記のように長辺(今回は横)を 100 で指定する

$ go run main.go test.jpg out.jpg 100

すると下記のような 100 × 56 が出来上がる

元画像 切り取り後
f:id:shiro-16:20200605114910j:plain f:id:shiro-16:20200605115217j:plain

終わりに

前回の記事の内容と組み合わせると golang だけで様々な画像加工ができるのではないでしょうか?
計算とかめんどくさいという人は https://github.com/nfnt/resize を使うのも良いかもしれません。

Golang で ImageMagick を使わずに画像をいじる

はじめに

最近動的に画像を変換するみたいなことをやっていて ImageMagick を使えば簡単にできるんですが
Golang の場合 ImageMagick を使わなくても様々な画像の加工が可能なので ImageMagick を使わずにやった時のメモ

画像形式の変換

最初は画像形式の変換の説明

jpg を png にしたり webp にしたりなどです。

ちなみに Golang では webp の Encode は標準でサポートされていないので今回は https://github.com/chai2010/webp を使用します。

package main

import (
	"flag"
	"fmt"
	"github.com/chai2010/webp"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"os"
	"strings"
)

func main() {
	flag.Parse()
	args := flag.Args()

	f, err := os.Open(args[0]) // 元画像読み込み
	if err != nil {
		fmt.Println("open:", err)
		return
	}
	defer f.Close()

	img, _, err := image.Decode(f) // 元画像デコード
	if err != nil {
		fmt.Println("decode:", err)
		return
	}

	fso, err := os.Create(args[1]) // 変換後画像作成
	if err != nil {
		fmt.Println("create:", err)
		return
	}
	defer fso.Close()

	slice := strings.Split(args[1], ".")

	switch slice[len(slice)-1] { // 出力画像の拡張子によってエンコードを変える
	case "jpeg", "jpg":
		jpeg.Encode(fso, img, &jpeg.Options{})
	case "png":
		png.Encode(fso, img)
	case "gif":
		gif.Encode(fso, img, nil)
	case "webp":
		webp.Encode(fso, img, &webp.Options{Lossless: true})
	default:
	}
}

こんなコードを用意して下記のように実行すると変換されます。

$ go run main.go test.jpg hoge.png
$ go run main.go test.jpg hoge.webp
元画像 変換後(gif)
f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200529113617g:plain

画像の切り抜き

続いて画像の一部を切り抜く方法を説明

package main

import (
	"fmt"
	"image"
	"image/jpeg"
	"os"
)

type SubImager interface {
	SubImage(r image.Rectangle) image.Image
}

func main() {
	f, err := os.Open("test.jpg")
	if err != nil {
		fmt.Println("open:", err)
		return
	}
	defer f.Close()

	img, _, err := image.Decode(f)
	if err != nil {
		fmt.Println("decode:", err)
		return
	}

	fso, err := os.Create("out.jpg")
	if err != nil {
		fmt.Println("create:", err)
		return
	}
	defer fso.Close()

	cimg := img.(SubImager).SubImage(image.Rect(50, 0, 150, 100))

	jpeg.Encode(fso, cimg, &jpeg.Options{Quality: 100}) // Quality を指定しないと荒すぎる画像が出来上がるよ
}

image.Rect で横は 50px ~ 150px 縦は 0px ~ 100px まで切り取るという指定をしています。
こうして出来上がったのが下記の画像

元画像 切り取り後
f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200529115148j:plain

画像の合成

最後に画像の合成

2 つの画像を重ね合わせるなどして合成する方法を説明します。

package main

import (
	"fmt"
	"golang.org/x/image/draw"
	"image"
	"image/color"
	"image/jpeg"
	"os"
)

type SubImager interface {
	SubImage(r image.Rectangle) image.Image
}

func main() {
	f, err := os.Open("test.jpg")
	if err != nil {
		fmt.Println("open:", err)
		return
	}
	defer f.Close()

	img, _, err := image.Decode(f)
	if err != nil {
		fmt.Println("decode:", err)
		return
	}

	fso, err := os.Create("out.jpg")
	if err != nil {
		fmt.Println("create:", err)
		return
	}
	defer fso.Close()

	m := image.NewRGBA(image.Rect(0, 0, 200, 200)) // 200x200 の画像に test.jpg をのせる
	c := color.RGBA{0, 0, 255, 255} // RGBA で色を指定(B が 255 なので青)

	draw.Draw(m, m.Bounds(), &image.Uniform{c}, image.ZP, draw.Src) // 青い画像を描画

	rct := image.Rectangle{image.Point{25, 25}, m.Bounds().Size()} // test.jpg をのせる位置を指定する(中央に配置する為に横:25 縦:25 の位置を指定)

	draw.Draw(m, rct, img, image.Point{0, 0}, draw.Src) // 合成する画像を描画

	jpeg.Encode(fso, m, &jpeg.Options{Quality: 100})
}

こうして出来上がったのが下記の画像

元画像 合成後
f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200529122847j:plain

上記では青い画像を作成して合成を行なったがもちろん既存の画像を使うことも可能で下記のように書く

// 上記の defer fso.Close() まで略
        f2, _ := os.Open("hoge.jpg") // 元になる画像
        img2, _, _ := image.Decode(f2)

        rgba := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{200, 200}}) // RGB形式の画像を用意する

        draw.Draw(rgba, image.Rectangle{image.Point{0, 0}, img2.Bounds().Size()}, img2, image.Point{0, 0}, draw.Src) // 元になる画像を描画する

        rct := image.Rectangle{image.Point{25, 25}, img2.Bounds().Size()} // 元画像への描画位置を決める

        draw.Draw(rgba, rct, img, image.Point{0, 0}, draw.Src) // 乗せる画像を描画

        jpeg.Encode(fso, rgba, &jpeg.Options{Quality: 100})
}

これで出来上がるのが下記の画像

元画像 合成する画像 合成後
f:id:shiro-16:20200529125808j:plain f:id:shiro-16:20200529113555j:plain f:id:shiro-16:20200529125837j:plain

終わりに

ちょっと長くなってしまったので今日はここまで
続きは次週とかに書くかもしれない

AWS CloudFront で Cache を Purge する(PHP/Ruby編)

はじめに

最近 CloudFront を使い始めたんですよね。以前は別の CDN を使っていたのですが

たまに Cache Purge したいよねってなることはあるわけでその処理を PHPRuby で書いたのでメモ
数年ぶりに PHP を書いたのですが...

ついでに AWS console からの方法も書いておく

Cache Purge するアカウントに cloudfront:CreateInvalidation の権限が必要なので注意

前提

既に CloudFront の Distribution は作成済み

PHP

まずは aws-sdk を追加

$ composer require aws/aws-sdk-php

後は PHP から使ってあげるだけ

use \Aws\CloudFront\CloudFrontClient as AwsCFClient;

$client = AwsCFClient([
  'region'  => /* region を入れるのだが CloudFront  では region の概念がないのでどこでも良いっぽい*/,
  'version' => '2016-01-28',
  'credentials' => [
    'key'    => /* AWS_ACCESS_KEY_ID を入れる*/,
    'secret' => /* AWS_SECRET_ACCESS_KEY を入れる*/
  ]
]);

$paths = ["/hoge", "/test"]; // purge したい cache の path
$result = $client->createInvalidation([
  'DistributionId' => /* ここに作成済みの Distribution id を入れる*/,
  'InvalidationBatch' => [
    'CallerReference' => time(),
    'Paths' => [
      'Quantity' => count($paths),
      'Items' => $paths
    ]
  ]
]);

Ruby

aws-sdk の gem の追加から

$ vi Gemfile
+ gem 'aws-sdk'
$ bundle install
# or
$ gem install aws-sdk

後は Ruby から使ってあげる

client = Aws::CloudFront::Client.new(
    region: REGION,
    access_key_id: AWS_ACCESS_KEY_ID,
    secret_access_key: AWS_SECRET_ACCESS_KEY
)

res = client.create_invalidation({
  distribution_id: distribution_id,
  invalidation_batch: {
    paths: {
      quantity: paths.count,
      items: paths
    },
  caller_reference: Time.now.to_i.to_s
  }
})

res&.invalidation&.status == 'InProgress'

console で確認

指定した Distribution の詳細を開き Invalidations の tab を開くと In Progress と表示されているはず。

f:id:shiro-16:20200522111914p:plain

完了すると Completed になる。

もちろんこの画面からも cache purge 出来る。

画像の Create Invalidation をクリックすると paths を入れるフォームが表示されるので入力して Invalidate を押すだけ。

最後に

caller_reference はユニークである必要があるので並列で呼び出すとかの場合はマイクロ秒単位にするとかもしくは別の方法で生成するなどした方が良いかと。

cache purge はお金がかかるからお気をつけを(無料枠もあります)

docs.aws.amazon.com

リモートワークが長くなったのでマンションを買った

はじめに

完全に出落ち感あるタイトル...タイトルは完全に釣りでリモートワークが長くなったからではなくタイミング的にそんな感じになっただけです。

なぜ購入したか?

以前住んでいたマンションの契約更新時期が今年(2020 年) 9 月末だったのだけれど、もう少し広いところに住んで飼っている犬を家の中で走り回らせたいなぁと思い出したのが昨年の夏くらい。

そこから 2LDK で職場にある程度近い賃貸を探してみたのだけれど、 20 万以上/月という感じで出せなくはないが高いなぁ...という感じだった。

そこで買ったらどうなるん?と思いつき調べたらローンだけなら 15 万/月もかからないじゃんということがわかり買うかとなった。

購入時期的には 2020 年はオリンピックがある(はずだった)ので開催中に探すのは色々大変だろうなというのがあったので開催前に買うのが良いかという感じでこのタイミングになったのだった。(コロナの影響で結局オリンピックは延期になったわけだが...)

購入までの流れ

物件調査

昨年の夏 ~ 今年の 1, 2 月まで主にインターネットで自分の条件に合う物件を探していた。
cowcamo/SUUMO/HOME'S あたりをよくみていた気がする。

内見

今年に入って実際に見てみるか〜となって 2 月末 ~ 3 月中旬は十数件の物件を内見してみた。

内見するとこの平米数ならこのくらいの広さなのか〜ふむふむとなり平米数で言うなら最低このくらい欲しいなというのも決まり良かった。

物件決定

そんなこんなで 3 月中旬に内見した今の家がほぼ全ての条件をクリアしていたので購入することを決定した。
この段階でローンの事前審査をした気がする。

諸々契約

それ以降 は大量の書類に個人情報とハンコを押しまくっていた気がする。

ローンの事前審査が通ったら、そのマンション買いますよ的な申し込みをしつつローンの本審査を行った気がする。
ローンの審査は 1 週間ほどで終わるはずで本審査が無事通ったので売買契約の締結やローンの契約/支払いなどを 3 月末 ~ 4 月中旬に終わらせて購入完了となった。

その後

その後は前の家の退去申請などをやったり引っ越しの手配やら荷造り、新居で買うものを決めたりなどこれは賃貸だろうと購入しようとあまり差は無さそう。

そんなこんなで GW 前に無事に引っ越しができ GW 中は新居用に買った様々なものが届いたのでそれの配置など行って過ごしていた。

最後に

当初の目的通り、引っ越し後は犬が家の中を走り回っていて可愛い。

これは床暖が好きになっている様子
www.instagram.com


また第二の目的だった書斎も出来た。
f:id:shiro-16:20200508114126j:plain

ということで最後にアレです。
www.amazon.co.jp

kubeval を CI に組み込んで kubernetes の yaml をチェックする

はじめに

kubeval is これ
github.com

1 年くらい前に kubernetesyaml の形式が正しいのか?を CI とかでチェックしたいな〜という気持ちになり色々なツールを調べはしたんですが、
その時はいまいちしっくりこなかった(記憶が曖昧)で導入を見送ったのですが下記の資料を見てイケるやんと思ったので導入した。

speakerdeck.com

資料にもある通り kubectl apply --dry-run とかで似たようなことができるが、CI から cluster に接続とかするのがめんどくさかったというのもあり --dry-run も採用しておらず kustomize build が正常に終了するということだけを test していました。

しかもこの資料見る前日にまさに数字と文字列の違いで apply に失敗ということが実際に起きてたので入れとこってなった。

CI に組み込む

今回は kustomize build した結果を kubeval に送って validation をしてもらう

まずは Dockerfile

kustomize と kubeval が使える Dockerfile を用意する

FROM golang:1.12-alpine
RUN apk add git gcc g++ libc-dev && \
    wget  https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v3.2.3/kustomize_kustomize.v3.2.3_linux_amd64 && \
    chmod +x ./kustomize_kustomize.v3.2.3_linux_amd64 && \
    mv kustomize_kustomize.v3.2.3_linux_amd64 /usr/local/bin/kustomize && \
    wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz && \
    tar xf kubeval-linux-amd64.tar.gz && \
    cp kubeval /usr/local/bin

version とかはまぁいい感じに

drone.yaml

社では drone を使っているので drone.yaml を書いていく

---
clone:
  depth: 1
build:
  image: xxxxxxxxxx # 上記の Dockerfile を build した image を指定
  auth_config: &auth_config
    username: xxxx
    password: xxxxxxx
    email: xxxxxx
  commands:
    - for env in `ls "overlays"` ; do # env 毎に kustomize build を行ってチェックしたい
        kustomize build "overlays/${env}" | kubeval;
      done

あとは branch を push した際に CI が走るように設定を行えば終わり.

最後

skip-kinds のオプションを使えば特定のリソースを除外することもできたりします。
そこらへんは docs に書いてあるので参考にすると良いかと

EKS で Auto Scale 導入した後のトラブルを解消する

はじめに

shiro-16.hatenablog.com

Auto Scale 導入に関しては上記を参照。

今回のトラブルとは

Auto Scale によって workernode 自体は増えたのにその workernode に pod が作られた時に IP が の状態のままで pod が起動しないというもの。

調査をする

正常に pod が起動する workernode と正常に pod が起動しない workernode を比べると後者は セカンダリプライベート IP の欄に IP がない事がわかった。さらにその workernode のサブネットをみると 利用可能な IPv4 アドレス が 0 になっているというのが確認できた。

数時間後にそのサブネットの 利用可能な IPv4 アドレス をみると数字が増えていることわかった。

つまり workernode の セカンダリプライベート IP に割り当てられた IP が workernode が Auto Scale によって削除された後に再利用可能になるまでに時間がかかり過ぎて IP が足らなくなるという現象ということを突き止めた。

対応する

利用可能な IPv4 アドレス を増やす

上記の現象を AWS の中の人に共有した際に「もともと大量の IP を用意して使うものなので...」(意訳)ということを言われて、ですよね〜となったので単純に workernode に割り当てられるサブネットの IPv4 CIDR を変更して利用可能な IPv4 アドレス を増やすということをやった。

これで上記のトラブルは発生しなくなった。がしかし仮に今後アクセスが増えて workernode の数が大量に必要になった場合などに同じ現象に遭遇しないとも限らないということでもう一つ対応を行ってみた。

ネットワークインターフェイスを定期的に掃除する

workernode(というか EC2) の IP 周りはネットワークインターフェイスを見る事によって現在の status を確認できる。

status が available のものは定期的に削除されている模様というのがわかった。がしかしこの間隔が長すぎるので上記の問題が発生する

という事でこちら側で短い間隔でネットワークインターフェイスを掃除するという対応を行った。

Kubernetes の CronJob でやって貰った。

ネットワークインターフェイスを掃除するスクリプト

まずは AWS SDK for Ruby | AWS を使用してネットワークインターフェイスを掃除するスクリプトを作成

require 'aws-sdk'

ec2 = Aws::EC2::Client.new()
filters = {
  filters: [
    {
      name: :status,
      values: [:available]
    },
    {
      name: :description,
      values: ["aws-K8S-i-*"] # 今回は workernode 用に作成されたものだけ削除する
    }
  ]
}
ec2.describe_network_interfaces(filters).network_interfaces.each do |network|
  ec2.delete_network_interface({network_interface_id: network.network_interface_id})
end

これだけ

CronJob の yaml を用意する


15 分ごとに上記のスクリプトを実行する CronJob を用意する。
aws-sdk の install が遅いので aws-sdk 入りの image を用意しても良いと思います。(自分はそうしている)

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: network-interface-releaser
spec:
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - command:
            - gem
            - install
            - aws-sdk
            - &&
            - ruby
            - network-interface-releaser.rb
            env:
            - name: AWS_REGION
              value: ap-northeast-1
            image: ruby:2.7.0
            name: network-interface-releaser
          restartPolicy: Never
  schedule: '*/15 * * * *'
アクセス権限周りの設定

下記のアクセス権限を持つユーザを追加して アクセスキー ID などを kubernetes の Secret にして使う(この場合上記の yaml に env などの追記が必要)か workernode の IAM に追加すると CronJob からネットワークインターフェイスの操作が可能になる。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DeleteNetworkInterface",
                "ec2:DescribeNetworkInterfaces"
            ],
            "Resource": "*"
        }
    ]
}
最後に CronJob の yaml を apply する

EKS の cluster に yaml を apply すれば定期的に CronJob がネットワークインターフェイスを掃除してくれて綺麗な状態を保ってくれるようになる。

さいごに

個人の見解ですが、調査する時に正常に動作しているものと正常に動作していないものが既に両方存在してると勝ったなって思いますよね。

AWS SAM CLI を使って AWS Lambda をいい感じに管理する

はじめに

github.com
今回使うのは上記

Lambda 環境をローカルで動かしたり出来るので動作確認とかがすごく便利になるやーつ

実際に使う

インストール

今回は mac にインストールする

$ brew tap aws/tap
$ brew install aws-sam-cli
$ sam --version
SAM CLI, version 0.41.0

サンプルのアプリを作ってみる

今回は sam-app という名前のアプリを Golang で作成します。

$ sam init --name sam-app --runtime go1.x
$ cd sam-app
$ tree ./
├── Makefile
├── README.md
├── hello-world
│   ├── main.go
│   └── main_test.go
└── template.yaml

ローカルで動作確認

まずはローカルで動作確認をしてみる

$ GOOS=linux GOARCH=amd64 go build -o hello-world/hello-world ./hello-world # まずは build
$ sam local start-api
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
# http://127.0.0.1:3000/hello にアクセスするとレスポンスが返ってくる
# 初回は Image の DL が走るので時間がかかる

デプロイする

$ sam deploy -g # 初回は対話式で色々入力したいので -g をつける
# 色々な IAM 周りの権限を作ってくれる
# samconfig.toml というファイルが作られる
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OutputKey-Description                                                                                OutputValue
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
HelloWorldFunctionIamRole - Implicit IAM Role created for Hello World function                       arn:aws:iam::0000:role/sam-app-HelloWorldFunctionRole-xxxxx
HelloWorldAPI - API Gateway endpoint URL for Prod environment for First Function                     https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/
HelloWorldFunction - First Lambda Function ARN                                                       arn:aws:lambda:ap-northeast-1:0000:function:sam-app-HelloWorldFunction-xxxx
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
$ sam deploy # 2 回目以降

コレで deploy が完了するので上記の HelloWorldAPI - API Gateway endpoint URL for Prod environment for First Function に記載されている URL にアクセスするとレスポンスが返ってきます。

/hello/ をやめてみる

endpoint 変更したいなって気持ちなので変更してみる

$ git diff
diff --git a/template.yaml b/template.yaml
--- a/template.yaml
+++ b/template.yaml
@@ -22,7 +22,7 @@ Resources:
         CatchAll:
           Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
           Properties:
-            Path: /hello
+            Path: /
             Method: GET
       Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
         Variables:
@@ -34,7 +34,7 @@ Outputs:
   # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
   HelloWorldAPI:
     Description: "API Gateway endpoint URL for Prod environment for First Function"
-    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
+    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/"
   HelloWorldFunction:
     Description: "First Lambda Function ARN"
     Value: !GetAtt HelloWorldFunction.Arn
$ sam deploy 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------OutputKey-Description                                                                                OutputValue
------------------------------------------------------------------------------------------------------------------------------------------------------------------------HelloWorldFunctionIamRole - Implicit IAM Role created for Hello World function                       arn:aws:iam::0000:role/sam-app-HelloWorldFunctionRole-xxxxx
HelloWorldAPI - API Gateway endpoint URL for Prod environment for First Function                     https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/
HelloWorldFunction - First Lambda Function ARN                                                       arn:aws:lambda:ap-northeast-1:0000:function:sam-app-HelloWorldFunction-xxxx
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
$ curl https://xxxx.execute-api.ap-northeast-1.amazonaws.com/Prod
Hello, 0.0.0.0

Golang の場合はどのアプリが使われるのか?

Golang の場合は build されたアプリが使われるのか勝手に build されるのか?という部分が少し謎だったので調べてみた。

$ git diff hello-world/
diff --git a/hello-world/main.go b/hello-world/main.go
--- a/hello-world/main.go
+++ b/hello-world/main.go
@@ -41,7 +41,7 @@ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyRespo
        }

        return events.APIGatewayProxyResponse{
-               Body:       fmt.Sprintf("Hello, %v", string(ip)),
+               Body:       fmt.Sprintf("Hello, %v", string("test")),
                StatusCode: 200,
        }, nil
 }
$ sam deploy 
$ curl https://xxxx.execute-api.ap-northeast-1.amazonaws.com/Prod
Hello, 0.0.0.0 # 変わってない
$ GOOS=linux GOARCH=amd64 go build -o hello-world/hello-world ./hello-world
$ sam deploy 
$ curl https://xxxx.execute-api.ap-northeast-1.amazonaws.com/Prod
Hello, test # 変わった

ということはどこかで build 後のバイナリを指定してるんだろうなと思って template.yaml を覗いてみたら Handler: hello-world と記述された部分があったのでもう少し実験

$ mv hello-world/hello-world hello-world/hoge
$ git diff template.yaml
diff --git a/template.yaml b/template.yaml
--- a/template.yaml
+++ b/template.yaml
@@ -15,14 +15,14 @@ Resources:
     Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
     Properties:
       CodeUri: hello-world/
-      Handler: hello-world
+      Handler: hoge
       Runtime: go1.x
$ sam deploy
$ curl https://xxxx.execute-api.ap-northeast-1.amazonaws.com/Prod
Hello, test # 動いてる

ということでバイナリの名前を変えて template.yaml 内で指定していた場所を書き換えたら、元どおりに動いているので推測通り Handler 部分で指定されているというのが確認できた。

最後に

まだ AWS SAM CLI について調べきれていないので触りながら動作を確認してみた。

最終的にやりたいことは example.com > CDN > Lambda といった感じで CDN を挟んでアクセスを Lambda に向けたいというものなのだけれど、CDN が CloudFront ならサクッと出来るが今回は諸々の事情から Akamai を使うので Akamai の設定でハマって進捗が悪くここまで...

続きは第二回のブログがあるかもしれない