gitkadoの気まぐれ日記

島根在住エンジニアが何かに興味を持ったらブログを更新します

aws sam initを試してみた(Go)

はじめに

gitkado.hatenadiary.jp

Goに興味が湧いたので、Goで試してみました。
実行環境はAWSのCloud9です。

開発環境準備

Cloud9使います!

Go

以下の手順にそってGoの環境を構築する。

gitkado.hatenadiary.jp

sam initを試す (Go)

$ sam init --runtime go1.x --name hello-sam
Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1

Quick start templates may have been updated. Do you want to re-download the latest [Y/n]: Y

# 以下のファイルが生成される
└── hello-sam
    ├── hello-world
    │   ├── main.go # lambda function
    │   └── main_test.go # unit test
    ├── Makefile 
    ├── README.md
    └── template.yaml

$ cd hello-sam

# go.mod作成
$ go mod init sample

# install
$ go get github.com/aws/aws-sdk-go

# test
$ go test ./...
ok      hello-sam/hello-world   (0.852s)

# build
$ GOOS=linux GOARCH=amd64 go build -o hello-world/hello-world ./hello-world

# Lambda実行(HelloWorldFuntion)
$ sam local invoke HelloWorldFunction

# APIサーバ起動
$ sam local start-api
$ curl 127.0.0.1:3000/hello

まとめ

Shimane.goもあるから、この調子でどんどんGo(&SAM)やってくぞ!
手っ取り早く取り組めて素晴らしい。

参考サイト

Hello SAM: AWS Golang Quickstart
Goの開発環境にAWS Cloud9をつこうてみる
GOPATH は適当に決めて問題ない

Go備忘録 -struct周辺-

Go備忘録 -struct周辺-

  • Goにはクラスがない。代わりにstructを使用する
  • structはクラスのように関数定義できない(初めての経験)

Example

struct定義

type Person struct {
    FirstName string
    LastName string 
}

// 変数定義
// フィールド値なし
var person Person
person := Person{}
// フィールド値順
person := Person{"Taro", "Sato"}
// KeyValue指定
person := Person{FirstName: "Taro", LastName: "Sato"}

struct継承

import "fmt"

type Creature struct {
    Age int
    Sex string
}

func (c Creature) AgeAge() int {
    return c.Age * 2
}

type Person struct {
    // CreatureStructのフィールド値を埋め込み
    Creature
    FirstName string
    LastName  string
}

person := Person{
    Creature: Creature{
        Age: 1,
        Sex: "man",
    },
    FirstName: "Taro",
    LastName:  "Sato",
}

fmt.Println(person)
fmt.Println(person.Creature)
// PersonでAgeが定義されているようにアクセスできる
fmt.Println(person.Age)
// Creatureで定義した関数もPersonに継承される
fmt.Println(person.AgeAge())
// 実行結果
{{1 man} Taro Sato}
{1 man}
1
2

struct関数定義

import "fmt"

type Person struct {
    FirstName string
    LastName  string
}

func (p *Person) Name() string {
    return p.FirstName + " " + p.LastName
}

person := &Person{"Taro", "Yamada"}
fmt.Println(person.Name())
// 実行結果
Taro Yamada

NG

type Machine struct {
    Name string
}

func (m Machine) Name() string {
    return m.Name
}
// 実行結果
// (ERROR: Structで定義している属性と同名の関数は定義できない)
type Machine has both field and method named Name

interface(ダックタイピング)

import "fmt"

type Person struct {
    FirstName string
    LastName  string
}

func (p *Person) Name() string {
    return p.FirstName + " " + p.LastName
}

type Machine struct {
    Name string
}

type Named interface {
    Name() string
}

func printName(named Named) {
    fmt.Println(named.Name())
}

// ケース1
person := &Person{"Taro", "Yamada"}
printName(person)
// ケース2
machine := &Machine{"PC"}
printName(machine)
// 実行結果
// ケース1
Taro Yamada
// ケース2 (ERROR: MachineStructにName関数が定義されてない)
*Machine does not implement Named (missing Name method)

Go備忘録 -defer-

Go備忘録 -defer-

  • 呼び出し元関数が終了(return)するまでdeferに与えた関数は評価されない。
  • deferに与えた関数の引数などは、その時点の値で実行される。
  • 呼び出し元関数でdeferを複数実行している場合は、あとで呼ばれたdeferが先に評価される。

実行

package main

import "fmt"

func main() {
    i := 1
    defer fmt.Println(i)
    i ++
    defer fmt.Println(i)
    i ++
    fmt.Println("func return")
}
// 実行結果
func return
2
1

参考

go-tour-jp.appspot.com

Go備忘録 -for-

Go備忘録 -for-

Goで繰り返し処理を行う場合はforを使うしかありません。

forの色々

sum := 0
// 初期化; 条件(真の間繰り返し); 後処理
for i := 0; i < 10; i++ {
    sum += i
}
fmt.Println(sum)
sum := 1
// 初期化と後処理を省略
for sum < 1000 {
    sum += sum
}
fmt.Println(sum)
// すべて省略すると無限ループ
for {
    // 無限ループで実行する処理を記述
    fmt.Println("Running...")
}
strArray := [3]string{"hoge", "fuga", "buzz"}
// 要素を1つずつ取り出す(iにはidx値が格納される)
// rubyでいうとeach.with_index
for i, s := range strArray {
    fmt.Printf("index: %d, name: %s\n", i, s)
}

参考

ほぼこの記事に書いてありました。

Go備忘録 -ポインタ-

Go備忘録 -ポインタ-

詳しい説明はこの記事に書いてありました。

ポインタ使い方

// 適当なオブジェクト(int型)
obj := 100

// int型のポインタ格納用変数定義
var pointer *int

// int型のポインタ格納用変数定義+objのポインタを代入
var pointer *int = &obj
// 同上
pointer := &obj

// ポインタ領域に格納されている値を参照
*pointer // => 100

ポインタを使ってない場合

func add_num(num int){
  num++
}
num := 1
add_num(num)
add_num(num)
num // => 1

ポインタを使った場合

func add_num(pointer *int){
  *pointer++
}
num := 1
add_num(&num)
add_num(&num)
num // => 3

ポインタ領域確保

GCが組み込まれているので手動で領域解放する必要はない

// int型のポインタ領域を確保
var pointer *int = new(int)
// 同上
pointer := new(int)

Question (保留事項)

  • ポインタ領域を確保しておく事で何が嬉しいの?
  • 領域が足りていない場合にどんな問題が起こり得るの?

lambdaからpsqlおよびpg_dumpを実行する

lambdaからpsqlおよびpg_dumpを実行する

Dockerコンテナ起動

  • 目的はpostgresqlソースを取得すること
  • lambda実行環境を再現したDockerImageじゃないとOS依存部分でコケる
FROM lambci/lambda:build-ruby2.5
# postgresql install
RUN yum install -y postgresql-devel
CMD "/bin/bash"
$ docker build -t lambda-ruby2.5-psql .
$ docker run --rm -it -v $PWD:/var/task -w /var/task lambda-ruby2.5-psql

postgresqlソース取得

  • /var/taskにソースを集約させる
  • デプロイされるlambdaのソース一式は/var/taskに配置される
$ mkdir /var/task/lib
$ mkdir /var/task/lib64
# デフォルトでpostgresql9.2がインストールされたためpgsql92
$ cp -a /usr/lib64/pgsql92 /var/task/lib64/pgsql92
# pgsql92で使用しているのがlibpq.so.5.5
$ cp -a /usr/lib64/libpq.so.5.5 /var/task/lib/libpq.so.5.5

libpq.soへのシンボリックリンク作成【ハマりポイント】

  • libpq.solibpq.so.5シンボリックリンクを作成する
    • psqlpg_dumpが内部でlibpq.soを呼び出しているため
  • /var/task/libに作成する
    • 環境変数LD_LIBRARY_PATH/var/task/libが含まれるため
# シンボリックリンク作成
$ ln -s /var/task/lib64/pgsql92/libpq.so /var/task/lib/libpq.so
$ ln -s /var/task/lib/libpq.so.5.5 /var/task/lib/libpq.so.5

lambdaから呼び出し

  • 上で/var/taskに配置/作成したソースと下のrbファイルを一緒にlambdaにデプロイして実行する
  • /var/task/lib64/pgsql92/bin/psqlシンボリックリンク作ればpsqlで呼び出せそうです
require 'json'

def handler(event:, context:)
  # psqlコマンドの確認としてバージョン出力させます
  { statusCode: 200, body: JSON.generate(`/var/task/lib64/pgsql92/bin/psql --version`) }
end

# => {"statusCode":200,"body":"\"psql (PostgreSQL) 9.2.24\\n\""}

まとめ

Linuxのフォルダ構成ライブラリ読み込みについて初めて調べました。
途中投げ出しかけましたが、実現できてよかった。

成果物

https://github.com/gitkado/lambda-psql-shell

ECSデプロイにECRを使ってみた(1/2:ECRにImage作成)

はじめに

以前はオンプレ環境のDockerを想定した使い方を記事にしましたが、
今回はECR(ECS)の使い方について記事にしていこうと思います。

  • ECS/ECRとは
  • ECRにリポジトリ作成
  • DockerfileからDockerイメージ作成
  • Containerデプロイの流れ(ECS)

ECS/ECRとは

ECS(Elastic Container Service)

  • コンテナオーケストレーションサービス
  • DockerコンテナをAWS上で管理する
  • ホストサーバの準備も不要
  • 他のAWSサービスとの連携も容易に行える

【公式】Amazon Elastic Container Service とは
Amazon EC2 Container Service(ECS)の概念整理
AWS Black Belt Online Seminar 2016 Amazon EC2 Container Service

ECR(Elastic Container Registry)

  • Dockerレジストリサービス
  • Dockerイメージ(Dockerfileやら諸々)をAWS上で管理する
  • ECSと統合されているのでコンテナ用のイメージをECRから取得できる
  • AWSに限らずローカルマシンやオンプレ環境からも容易にアクセスできる

【公式】Amazon Elastic Container Registry とは
【公式】Amazon Elastic Container Registry の特徴

ECRにリポジトリ作成

  1. ECRサービス画面の「Get Started」をクリック f:id:gitkado:20190315000513p:plain
  2. 作成するECRのリポジトリ名を入力して「リポジトリの作成」をクリック f:id:gitkado:20190315000602p:plain
  3. はい、完成(超簡単) f:id:gitkado:20190315000606p:plain

DockerfileからDockerイメージ作成

Dockerfileの作成についてはこちらを参照してください。

ECRリポジトリ画面右上の「プッシュコマンドの表示」から
以下の手順について確認することができます。

# sample/nginx = ECRリポジトリ名

# Dockerfile配置フォルダに移動
$ cd ecr/sample-nginx
# Dockerのログインコマンド取得&実行
$ (aws ecr get-login --no-include-email --region ap-northeast-1)
$ docker login -u AWS -p xxxxxxxx https://xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com
# Dockerイメージをbuild
$ docker build -t sample/nginx .
# buildしたイメージをECRにpushできるようにタグ付け
$ docker tag sample/nginx:latest xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/sample/nginx:latest
# タグ付けしたイメージをECRにpush
$ docker push ********.dkr.ecr.ap-northeast-1.amazonaws.com/sample/nginx:latest

# ECRからイメージをlocalにpull
$ docker pull ************.dkr.ecr.us-east-1.amazonaws.com/sample/nginx:latest

Containerデプロイの流れ

(キャプチャを使って説明する予定)

未着手

まとめ

未着手

参考

awscli インストールと設定
Docker for Mac インストール 初めてのECR