AWS Lambdaでトリガーが削除できなくなった時
例えば API GatewayからLambdaを連携させた後、連携を削除したい場合は
Lambdaのトリガーを削除したあとに API Gatewayから連携を削除すればよいのですが
逆をやってしまうと。 先に API Gatewayから削除した場合は Lambdaにトリガーが残ったまま削除できないという不具合がおこります
どう見ても AWS管理画面の不具合ですが、システム的には消せるので
AWS Clientでコマンドをうてば消せます
aws lambda remove-permission を使えば消せるのですが、sidを手に入れるため aws lambda get-policyを使います
$ aws lambda get-policy --function-name LambdaのARN
ARNはLambda管理画面の右上に arn:aws:lambda:ap-northeast-1:ほげほげ・・ の形で表示されています
このコマンドを実行すると Jsonが返ってきます
そこで消したいものの sidを探し
$ aws lambda remove-permission --function-name LambdaのARN --statement-id 取得したsid
これで消える
近況
最近は縁あって、大型案件のサーバインフラ開発しています
みんな技術力が高い人たちなので安心です
ただ、私の事をすごい期待しているようで、基本的な役回りとしては
スケジュール的に厳しいため
私以外のメンバーは、モノリシックな LAMP環境での枯れたシステムを作り
私は マイクロサービス、サーバーレス、NoSQL・・・ 等の 新しい設計でサブシステムを作る
そんな感じです
まずは現行システムを理解するために PHPでのモノリシックなAPPサーバのチケットをこなし
ChatOps、DevOpsを Python、Golangを使って書き
Docker関連を修正したり 色々とこなし
現在は マイクロサービス化をすすめている
API Gatewayに関しては
AWSの API Gatewayが基本だと思うけど、APIIのMegre等がなさそう
その他 基本的にURLを転送する事とLambdaを起動する事ぐらいしかできない
機能がすくない・・
Kong。最も人気っぽい。当然 ResponseのMegreが出来る
Lambdaを起動するプラグインあるぽい
KrakenD。速い。Lambda起動プラグインみつからない
つまり メリット、デメリット多い。
また、重要になるNoSQLはどれにするか決まってない
そんなかんじで、サーバレス、マイクロサービス化してます
Golang 試食 Part2
Class
そんなものはない
が、C言語で使うような構造体ポインタを明示的に渡す事で、ステートを持たせ同じことが出来る
// 構造体の作成。C言語と違い関数の定義は必要ない hoge:= &Hoge{ name: "Name", } // メソッドを呼ぶ hoge.Print()
// Print関数の定義。 h *Hogeで Thisポインタ的なものを定義 func (h *Hoge) Print() { // h.name と、明示的にインスタンスを指定する必要がある log.Printf("name= [%s]", h.name) }
その他、インタフェース的なものや、派生的なものもあり、ダックタイピングではあるがオブジェクト指向のようなことができる
Goのオブジェクト指向に関しては、一度まとめて整理したいところ
Goルーチン
Go言語の特徴であるGoルーチン
目的は 並列処理である
ライトウエイトプロセスを使い、シングルスレッドで非同期に処理を行う
go ほげほげ でライトウエイトプロセスを作成する
非同期なのでこの命令を読んだあと即座に処理が戻ってくるので、チャンネルを使い GoルーチンをWaitする
大雑把に Goルーチンが処理を終えシグナルを出すまでWaitすると思っていいだろう
例題だしたいが、後ほどちゃんと調査したい
WaitGrout
wait := sync.WaitGroup{} wait.Add(1) go func() { defer wait.Done() ほげほげ }() wait.Wait()
Channel
ch := make(chan struct{}) go func(){ ほげほげ ch <- struct{}{} }
プラグイン
Golangはプラグイン(共有ライブラリ)機能がある
動的に共有ライブラリ(.so .dll的な)を読み込むことが出来るので
本体をビルドしなおさずに機能の追加、修正が出来る
ただし、現在は64Bit Linuxでしか対応していないため、Windowsネイティブではプラグインを作れない
// pluginライブラリを使い 読み込む p, err := plugin.Open("path to .so") if err != nil { log.Printf("fail to load plugin") return } // hoge関数を探す。もちろん メンバ変数も取得可能 hoge, e := p.Lookup("hoge") if e != nil { log.Printf("fail to Lookup 'hoge'") return } // hoge関数を呼ぶが、interface型なので Castして使う必要がある。今回は func(string) string 型の関数を呼び出す hoge.(func([]string) string)("var")
interface型、関数ポインタの型キャスト、その他色々と面倒ではあるが便利
ビルド方法は
go build --buildmode=plugin
と、 buildmodeを指定すれば良い
Golang 試食 Part1
仕事で1か月間に新しく6言語を使ったのだが、その中で最も触ったのがGo
色々と想像と違ってたので、Goを勉強始めるあたりから書いていく
使う前の印象(全然間違えてた)
天才技術者エリート集団 Googleの作った最高の言語
コンパイル言語で、C言語並みの実行速度と スクリプト言語並みの作業効率
ゴルーチン等の、高速な非同期をつかった次世代Web言語
ひゃっほ~~
目的
DevOps目的で、チャットBOTを作る
プロジェクトに使う各種ツールのURLやディレクトリを教えたり
新人が入った際のアカウント追加をサポートしたり
ソースコードのCIや ビルドを行ったり
Deployを行ったり
メトリクスのアラート出したり
・・・
色々なオペレーションをするためのサンプル
ファーストターゲット
Slackで動くBOT
AWSのECR上にUpされたコンテナのリスト表示、Tag指定でDeployする
SlackBot
Slack側の設定
Slack側の設定はほかの人のブログみてもらおう
全容
今回は github.com/nlopes/slack を利用する
ので パッケージで上記を指定する
Golangはコンパイルエラーが厳しく、使ってないものをimportするとコンパイルしてくれない
main.go
package main import ( "log" "net/http" "os" "github.com/kelseyhightower/envconfig" "github.com/nlopes/slack" ) type envConfig struct { Port string `envconfig:"PORT" default: "3000"` BotToken string `envconfig:"BOT_TOKEN" required: "true"` BotID string `envconfig:"BOT_ID" require: "true"` ChannelID string `envconfig:"CHANNEL_ID" require: "true"` } func main() { os.Exit(_main(os.Args[1:])) } func _main(args []string) int { var env envConfig if err := envconfig.Process("", &env); err != nil { return 1 } client := slack.New(env.BotToken) slackListener := &SlackListener{ client: client, botID: env.BotID, channelID: env.ChannelID, } go slackListener.ListenAndResponse() if err := http.ListenAndServe(":"+env.Port, nil); err != nil { log.Printf("[Error] %s", err) return 1 } return 0 }
slackListenerは後で
package
package名mainは特殊である。
このパッケージのみ エントリーポイントとしてmain関数が機能する
import
importはとても便利。今までGoを使ってパッケージの依存関係なので悩んだことはなかった
(ただし duplicate importは 非常にダメだ・・・ )
Githubのリポジトリから直接持ってこれるのも素晴らしい
さすがGoogleだ
コード所感
文法は慣れるしかないが、C言語やJava等のメジャーな文法とは大きく変えてきている
全体的にGoはダックタイピングだ
変数や関数名の頭を小文字にすると外部パッケージからアクセスできないので、Publicな関数や変数は大文字スタートにする
また、C言語では暗黙的型変換されるもの(int32, int64 なども)も明示的にキャストしなければエラーである
エントリポイント
mainのところはおまじないとして なんとなく意味わかるでスルーしよう
envconfig
main_ 実質のメイン
envconfigは上記Githubからのパッケージで、環境変数を構造体にマッピングしてくれる
err := envconfig.Process("", &env);
envconfig.Process にて環境変数が設定されていれば構造体に入る
:= は、変数の宣言、定義、代入である(関数内でのみ使用できる)
例えば
hoge := func() は
var hoge = func()
とだいたい同等である
ただ、使った感じ微妙にスコープが違う
nlopes/slack
client := slack.New(env.BotToken)
にて、nlopes/slackのインスタンスを作成する
もちろん env.BotTokenは、環境変数 BOT_TOKENに入っているトークンだ
構造体作成
slackListener := &SlackListener{ client: client, botID: env.BotID, channelID: env.ChannelID, }
来た。。これは構造体の作成(new)を行っている
GoではClassは無く C言語のように 構造体を作成しそこにデータを保存し、関数に構造体を渡す
つまり 全くオブジェクト指向じゃない!
そして 構造体の作成方法が何種類もある
type Hoge struct{ Name string Age int64 } // newの場合は 宣言と同時に初期化できない hoge1 := new(Hoge) hoge1.Name = "HOGE" hoge1.Age = 10 // &でも構造体を作成できる hoge2 := &Hoge hoge2.Name = "HOGE" hoge2.Age = 10 // &は定義時に初期化可能 hoge2 := &Hoge{ Name: "HOGE", Age: 10 } // 構造体のフィールド名省略可能 hoge2 := &Hoge{ "HOGE", 10 }
つまり &を使ったものだけ覚えておけば良いと今の時点では思っている
Listen
go slackListener.ListenAndResponse() err := http.ListenAndServe(":"+env.Port, nil)
go の部分が Golangの特徴である ゴルーチン
ゴルーチンは非同期で実行されるため、 slackListener.ListenAndResponse() が終了しなくても
即座に次の行へ制御がいき http.ListenAndServeが実行される
slackListenerの中身は後で勉強するとして、大雑把にいうと WebSocketを待機していて、Slcakとアクセスをする
http.ListenAndServeにて Listenを行う
この時点の感覚
Goのコードはオブジェクト指向ではなく、予想以上に低レイヤーであった
C言語を書きやすくした Better C言語である
ゴルーチンは非同期をわかりやすく解決していて使いやすそう
ただ 予想より難しい言語で、PHPerが移住する場所としては不適切ではないか?
Web言語として優れているとは現時点ではいいにくい(あくまで Better C)
Elixir Phoenix 試食
はじめに
ここ1か月にたくさんの言語仕事で触っている
PHP(なぜか今まで触ったことなかった)、Golang、Python、Elixir、Rails、Scala、node(5年ぶり2回目) など
1か月程度なので知見がほとんどないけど、知識をまとめていく
作るもの
APIサーバのサンプル(評価)プロジェクト
Redis、MySQL、MongoDBなどのCRUDテスト
言語の評価
Docker構築
Docker構築
最初は OSから自分で入れた
とりあえずUbuntuで
FROM ubuntu:17.04 RUN apt-get update RUN apt-get install -y sudo wget RUN apt-get install -y language-pack-ja RUN wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && dpkg -i erlang-solutions_1.0_all.deb RUN apt-get update RUN apt-get install -y esl-erlang RUN apt-get install -y elixir ENV LANG ja_JP.UTF-8 ENV LANGUAGE ja_JP:ja ENV LC_ALL ja_JP.UTF-8 # Set app env ENV HOME /root WORKDIR /usr/local/src RUN apt-get install -y libssl-dev ncurses-dev RUN apt-get install -y sudo wget git tar bzip2 incron vim nodejs npm RUN apt-get install -y inotify-tools RUN yes | mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez #RUN mkdir /runnet #WORKDIR /runnet ENV PHOENIX_APP_NAME runnet ENV PHOENIX_APP_PORT 4000 ENV PHOENIX_APP_ROOT /${PHOENIX_APP_NAME} WORKDIR /$PHOENIX_APP_ROOT EXPOSE ${PHOENIX_APP_PORT} COPY . $PHOENIX_APP_ROOT WORKDIR /runnet/$PHOENIX_APP_ROOT # Compile phoenix(FOR dev) RUN yes | mix local.hex && yes | mix local.rebar && mix do deps.get, compile RUN mix deps.get RUN mix ecto.create RUN mix ecto.migrate # npm install #RUN npm install #RUN npm install -g brunch # Run Phoenix CMD ["/bin/bash", "-c", "mix phoenix.server"]
色々無駄なものがあるけど、とりあえず Erlang、Elixir、Phoenix と必要なものをセットアップ
結構めんどくさいね。イメージも大きいし
時間あったら Alpineにしようと思っていた
docker-composeも下記のかんじで
version: '2' services: app: build: . environment: MYSQL_ROOT_USERNAME: 'root' MYSQL_ROOT_PASSWORD: 'pass' MYSQL_HOSTNAME: 'mysql' MYSQL_PORT: '3306' ports: - '4000:4000' volumes: - .:/runnet links: - mysql mysql: image: mysql:5.7.10 environment: MYSQL_ROOT_PASSWORD: 'pass' ports: - '3306:3306' volumes: - mysql-data:/var/lib/mysql volumes: mysql-data: driver: local
PhoenixはデフォがPostgresだけど MySQLを入れた
router
scope "/", Runnet do pipe_through :browser # Use the default browser stack get "/", PageController, :index resources "/users", UserController end
controller
defmodule Runnet.UserController do use Runnet.Web, :controller alias Runnet.User def index(conn, _params) do users = Repo.all(User) render(conn, "index.html", users: users) end def new(conn, _params) do changeset = User.changeset(%User{}) render(conn, "new.html", changeset: changeset) end def create(conn, %{"user" => user_params}) do changeset = User.changeset(%User{}, user_params) case Repo.insert(changeset) do {:ok, _user} -> conn |> put_flash(:info, "User created successfully.") |> redirect(to: user_path(conn, :index)) {:error, changeset} -> render(conn, "new.html", changeset: changeset) end end def show(conn, %{"id" => id}) do user = Repo.get!(User, id) render(conn, "show.html", user: user) end def edit(conn, %{"id" => id}) do user = Repo.get!(User, id) changeset = User.changeset(user) render(conn, "edit.html", user: user, changeset: changeset) end def update(conn, %{"id" => id, "user" => user_params}) do user = Repo.get!(User, id) changeset = User.changeset(user, user_params) case Repo.update(changeset) do {:ok, user} -> conn |> put_flash(:info, "User updated successfully.") |> redirect(to: user_path(conn, :show, user)) {:error, changeset} -> render(conn, "edit.html", user: user, changeset: changeset) end end def delete(conn, %{"id" => id}) do user = Repo.get!(User, id) # Here we use delete! (with a bang) because we expect # it to always work (and if it does not, it will raise). Repo.delete!(user) conn |> put_flash(:info, "User deleted successfully.") |> redirect(to: user_path(conn, :index)) end end
model
defmodule Runnet.User do use Runnet.Web, :model schema "users" do field :name, :string field :email, :string field :encrypted_password, :string timestamps() end @doc """ Builds a changeset based on the `struct` and `params`. """ def changeset(struct, params \\ %{}) do struct |> cast(params, [:name, :email, :encrypted_password]) |> validate_required([:name, :email, :encrypted_password]) end end
migrateファイル
defmodule Runnet.Repo.Migrations.CreateUser do use Ecto.Migration def change do create table(:users) do add :name, :string add :email, :string add :encrypted_password, :string timestamps() end end end
ユーザ名、メールアドレス、パスワードを保存する、よくあるログイン認証まわり
細かく追ってないけど Routerでresources を指定すると、複数のメソッドに対応してくれるっぽい
そのあたりが自動的に controllerの index、new、create、show、update、delete等に対応される
MySQLとの連携もばっちりだし、そこまで言語的にも覚えにくくないと思う
Postgresで再構築
いちおうPostgresにて構築してみる。Redisも使う
同じようにDockerイメージ作る
docker-compose.yml
version: '3' services: db: image: postgres ports: - 5432:5432 redis: image: redis:latest ports: - 6379:6379 web: build: . container_name: "ages_app" volumes: - $PWD:/ages ports: - "4000:4000" links: - db - redis environment: DATABASE_USER: postgres DATABASE_PASSWORD: DATABASE_PORT: 5432 DATABASE_HOST: db REDIS_URL: redis://redis:6379 depends_on: - db - redis
今回はゲームサンプルを見据えて DB定義もそれっぽくする
router
scope "/", AgesApp do pipe_through :browser # Use the default browser stack get "/users", UserController, :index get "/user/:id", UserController, :show get "/", PageController, :index end
コントローラーのソース消失!
model
defmodule AgesApp.User do use AgesApp.Web, :model schema "users" do field :name, :string field :os_type, :string field :device_id, :string field :device_name, :string field :os_version, :string field :game_money, :integer field :ios_charge, :integer field :android_charge, :integer field :max_hp, :integer field :level, :integer field :exp, :integer field :max_deck_count, :integer field :max_deck_point, :integer field :max_friend, :integer field :tutorial_progress, :integer field :last_login_at, :naive_datetime timestamps() end @doc """ Builds a changeset based on the `struct` and `params`. """ def changeset(struct, params \\ %{}) do struct |> cast(params, [:name, :os_type, :device_id, :device_name, :os_version, :game_money, :ios_charge, :android_charge, :max_hp, :level, :exp, :max_deck_count, :max_deck_point, :max_friend, :tutorial_progress, :last_login_at]) |> validate_required([:name, :os_type, :device_id, :device_name, :os_version, :game_money, :ios_charge, :android_charge, :max_hp, :level, :exp, :max_deck_count, :max_deck_point, :max_friend, :tutorial_progress, :last_login_at]) end end
Migrate、Seedのコードも消失!
Postgresは上手くいったが、その後 Redisにセッション保存をしようと思い、Redis用のライブラリを探す
exredisが有名らしいので調べたところ、キー取得時にエラーがわからないという酷い実装のため変更
redixをパッケージに追加するとDependencyエラー
色々なバージョンを試してみたが、簡単にはいかない
その後、うちのメンバーに新人の参入可能性が高まったため、一度JavaScript覚えさせたいのと
最近のnodeは、ES6に対応したり、以前よりも複数人開発がしやすくなっているので
nodeの調査に変更した
Docker for windows 快適に使う
色々書いていたが Qiitaに投稿したので そっちのリンクを
とりあえず、私は6年前まではWindowsで開発
Linuxサーバ関連はVM使ったり、SSHログインしたり、Windowsネイティブで など色々だけど
基本的に面倒
5年前に iOS開発のためMac買ってからは、しばらくMac上で開発して快適だった
ところが、ここ数年 Dockerが便利で、Vagrant等も併用すると、WindowsでもMacでもほぼ同じに VM上で開発できる
そして、Windows10では Dockerがネイティブで動くようになった! Docker for Windowsの登場である
それに WindowsSubsystem for Linux ( WSL、 Bash on Windows) もかなり良くなり
また Dockerのファイルアクセスが、Windowsだけ10倍~のオーダーで爆速のため
最近は 完全にWindowsでサーバ開発している
今までの VM上でDockerを建てる方式は、何も困ることがなく、爆速になるのだが
おそらく Docker for Windowsのほうが 1~2割ほど速くなるはずなので、そちらを利用している
その場合は パスの問題やWindows固有問題で躓くことがあったが
今のところ全部回避できている
問題がおこるたびに 解決法を探していきます。私は人柱