ニクニクドットミー

カッコいいおっさんを目指すエンジニアの厳かなブログ

【AWS】5分で分かるAWS CodeDeploy

Codedeploy

久々にAWSのサービスを触ってみました。というのもデプロイの仕組みをカイゼンしたいなーと考えていて、社内でもどうしよか?と話をしていました。 現状はfabricというPython製のツールを使っているのですが、fabricを動かす為に環境を整えてあげる必要があったり、依存しているライブラリのバグが影響して、デプロイができない時があったりと、深刻な状況です。 AWS CodeDeployであればマネジメントコンソールから実行もできるので、環境構築の手間が省けるので、良いのではと思い軽く触ってみました。 触ってみた感想と、CodeDeployを簡単に説明したいと思います。 ※5分は釣りです^^;

AWS CodeDeployとは

AWS CodeDeployはEC2,AutoScaling,オンプレミスに特化したデプロイサービスです。 たしか2014年11月ぐらいに登場しました。ただ、サービスを提供しているリージョンがオレゴン、ヴァージニアのみ(2015/04/19時点)ですので、 東京リージョンはまだ使えないです。残念。はやく対応してほしいです。

リポジトリとして、S3、Githubが対応しています。今後はAWS CodeCommitと連携もするようです。 うちの会社ではリポジトリをStashで管理している為、対応していません。今後に期待!?

CodeDeploy用語

codedeploy-agent デプロイ対象となるサーバには必須になる。 S3からコピーしてインストールする必要がある。


$ sudo yum update
$ sudo yum install aws-cli
$ cd /home/ec2-user
$ aws s3 cp s3://aws-codedeploy-us-east-1/latest/install . --region us-east-1
$ chmod +x ./install
$ sudo ./install auto

appspec.yml アプリケーションのソースコードをどのディレクトリに配置するか、どのような権限で付与するかを決めるファイル。 またCodeDeployのライフサイクルに応じた処理をどうするかとかも記載する。必須

Application Name アプリケーション名。そのまんまだけど、実際につけるとするとプロジェクト名_環境名って感じになるのかな? hoge_productionとか。

Deployment Group Name デプロイ対象のグループ。グループの選び方はEC2のタグのKey,Value、AutoScalingGroup名、オンプレミス。 codedeploy codedeploy

Deployment Configuration デプロイ方法。デフォルトでは、

  1. CodeDeployDefault.AllAtOnce
    一度にすべてのインスタンスにデプロイする。最低1つのインスタンスにデプロイが成功した場合、全体のデプロイが成功したものとする。すべてのインスタンスへのデプロイが失敗した場合、全体のデプロイが失敗したものとする。
  2. CodeDeployDefault.OneAtTime
    インスタンス1つずつにデプロイする。全てのインスタンスにデプロイが成功した場合、全体のデプロイが成功したものとする。デプロイが失敗した時点で失敗と扱われるが、既にデプロイが成功したインスタンスはそのまま。
  3. CodeDeployDefault.HalfAtTime
    一度に総インスタンス数の半分までデプロイする。総インスタンス数の半分までデプロイが成功した場合、全体のデプロイが成功したものとする。全体のデプロイが失敗した場合でも、既にデプロイが成功したインスタンスはそのまま。

codedeploy_3

Service Role CodeDeployがEC2インスタンスにアクセスするロール。 codedeploy_4

デプロイの流れ

他のブログを参考にデプロイするリポジトリ作りました。 1.Applicationの作成 実は上の画像がほぼApplicationの設定の流れになります。ここではアプリの名前とデプロイ対象のグループを作る感じ。その枠を決めたイメージです。

2.デプロイするリビジョンの選択 1でCreate Applicationしたら遷移する画面。 deploy_settings 右のオレンジ枠がデプロイされたリビジョンの履歴が記載される所。ここから前のリビジョンをデプロイしたりする。つまりロールバックに使う感じ。 左の赤枠が、Deploy Group。ここからデプロイするソースを選択する。

deploy_settings_1.png

S3,Githubを選択する画面。 Create New Deployment

実はデプロイを実行する度にこの画面の入力はしないといけない。 リポジトリ名、コミットIDをいちいち入力するのがすごく面倒くさい。 自動化必須

Deploy結果とNGパターン

デプロイに成功するとこうなります。 deploy_success_1 デプロイ対象のインスタンスは2台あったので成功してますね。

次にNGパターン deploy_success_2 Deployment ConfigurationをCodeDeployDefault.OneAtTimeに設定していたので、一台ずつデプロイしています。 まず一台にデプロイしてFailedとなったので、残りのもう一台にはSkippedとなってデプロイ実行されてないです。 View All Instancesをクリック。

deploy_success_3 実際にどこで失敗したのかは赤枠を見ると分かります。 BeforeInstallで失敗したようです。 次に青枠のView Eventsを見てみましょう。

deploy_success_4 View Logsをクリック。

deploy_success_5 エラーメッセージが出てますね。よくわかりませんが。。。 この時どこに問題があったかというとappspec.ymlです。 appspec修正。 appspecのosの書き方が違ったので、修正

ほんとしょうもないミスなのですが、先ほどのメッセージからでは分かりにくいですよね。

まとめ

かなり雑になってしまいましたが、触ってみた感想を。

  • 自動化しないとツライ。コミットIDとかいちいち入力するのツライ
  • ロールバックするときにどのリビジョンがどういう修正だったかわからない。UIにタグが付けれて、デプロイする時に追加できると良いと思う。
  • codedeploy-agentの死活監視必須

参考

GitHubリポジトリと連携してAWS CodeDeployを使う(準備編) GitHubリポジトリと連携してAWS CodeDeployを使う(デプロイ編) AWS CodeDeploy を使って Rails アプリケーションをデプロイしてみた [新サービス] AWS CodeDeployを触ってみた #reinvent

kamakura.go#2に参加してきた!

28日に鎌倉で行われたGoの勉強会 kamakura.go #2に参加してきました。

第一回目の開催には所用があった為、参加できず、二回目開催してほしいなーとtwitterでつぶやいていたら、二回目開催されるとのことだったので、即申し込みしました。

ちなみにiichiさんで開催されているのですが、iichiさんのslackのGoのチャンネルに参加させて頂いてます。

勉強会としてはもくもく会で、最後に成果発表がありました。

成果発表時にやったこととkamakura.goの感想をslideにしたので、張っておきます。

会場はiichiさんで、場所は鎌倉にあります。小町通にあるので、休日は人がすごいです。

僕はよく鎌倉には行くので、人ごみは想定内でしたが、一緒に行った同僚の方は初鎌倉で面食らってました(笑)

とても天気がよくて鎌倉を散策するにはもってこいな日でした。

勉強会終了後の懇親会で鎌倉の焼き鳥屋に行ったのですが、そこの大将がすごく人に絡む人で、同僚が気に入られてしまい、ずっとその話で盛り上がりました。(おつかれさまでした^^;)

夜の鎌倉は初めてで、だいぶ人も減り、小町通もシャッターがすぐおりてました。昼間とはガラっと雰囲気変わるので驚きでした。

kamakura.go #3あれば参加します!

鎌倉で気になるお店も出来ました。尾崎、ブルールーム。これは行ってみたい!

やっぱ鎌倉いいとこだ。

【資料公開】社内LTで「プログラミング学習の3つのポイント」を話しました。

社内でLTを月に一回ぐらいのペースで開催していて、久々に登壇しました。

久々に発表用資料を作りましたが、頭の中を整理するのにちょうどいいですね。あとLTで意識しているのは「緊張感を楽しむ」ということ。 これは中学時代の部活(実はバレー部のキャプテン)で先生に教えてもらった言葉ですが、いまも意識しています。

大した話ではないですが、資料公開しておきます。 (今年は発表に力入れているので)

1.SIMPLE シンプルに継続する。

継続は力なりという言葉の通り、続けるとすこしずつ成長はする。 運動もそうで、ジムに週6日くらい通っている奥さんの方が、だんぜん動ける。。。頑張ろう。

2.EASY&SMALL 小さく簡単に作る。

少しでも動くとやっぱ楽しい。いきなりでかいものは作れないので、少しずつ進めていく。 そして、簡単なものでいいから作る。

3.READ コードを読む。

コード読むのはメリットだらけ。次第に読めてくると楽しいし、アイディアも湧く。 尊敬・憧れのエンジニアのコードだと尚更。

憧れのエンジニアはメルカリの久保さん。これはdotsSummit2015の発表を見て、カッコイイと思いました。 発表も冷静沈着な印象で、僕とは違う感じ(笑) slackboardの話で、イラっとしてGoでささっと書いたという話が印象的で、インフラでしっかりコードが書ける久保さんのようになりたいと思いました。 なので、slackboardのコードはgithubで読んでます。

ということで、pocket-for-Golangを頑張るぞ。

Goでtoml.DecodeFileしたときにハマったことメモ

182b65b9 539c 4d24 9b1f ee4a1a8756f1 TOMLという設定ファイルの為のミニ言語があり、それをGoで試してみた所、うまく読み込まれずにハマってしまったので、メモ。

config_test.go


package main

import (
    "github.com/BurntSushi/toml"
    "testing"
    "fmt"
)

type ConfToml struct {
    Keys SectionKeys `toml:"keys"`
}

type SectionKeys struct {
    Consumer_key string `toml:"consumer_key"`
    access_token string `toml:"access_token"`
}

// config の read
func readSettingsConfig(path string, config *ConfToml) {
    _, err := toml.DecodeFile(path, config)
    if err != nil {
        panic(err)
    }
}

func Test_readSettingsConfg(t *testing.T) {
    var conftoml ConfToml

    _, err := toml.DecodeFile("config.toml", &conftoml) 
    if err != nil {
        panic(err)
    }
    
    fmt.Println(conftoml.Keys)

    // Printlnする為に、あえてerrorにしている。
    if conftoml.Keys.Consumer_key != "aab" {
        t.Error("Not Match")
    }
    if conftoml.Keys.Consumer_key != "bbb" {
        t.Error("Not Match")
    }

}

config.toml


[keys]
consumer_key = "aaa"
access_token = "bbb"

実行結果


{aaa }
--- FAIL: Test_readSettingsConfg (0.00s)
        config_test.go:38: Not Match
        config_test.go:41: Not Match
FAIL
FAIL    command-line-arguments  0.005s

この太字にしたところがポイントで、これはtomlファイルを読み込んだ構造体をPrintlnしているのですが、aaaという値だけ格納されている状態です。 本来ならば、{aaa bbb}となるはずです。


type SectionKeys struct {
    Consumer_key string `toml:"consumer_key"`
    access_token string `toml:"access_token"`
}

このSectionKesyという構造体のフィールド名が小文字と大文字で違っているのが原因です。 どうやらDecodeFileはフィールド名が小文字だと値が設定されないようです。 ※Access_tokenと大文字で設定すると正しく値は設定されました。

恐らくですが、github.com/BurntSushi/tomlパッケージ外の構造体へのアクセス権限がない(エクスポートできない)のが原因かと思っています。 値が設定されず、やだな〜やだな〜こわいなーこわいな〜とハマっておりました。

謝った認識かもしれませんので、ご指導ご鞭撻のほどよろしくお願いします。

参考サイト

【個人メモ】設定ファイルフォーマットにはTOMLがいいのかも Go言語での構造体実装パターン

【AWS】ElastiCacheRedisをMulti-AZ構成にするとAOFの設定ができないっぽい?

Redis 今日はElastiCacheRedisを触ることが多かったので、本日二回目のElastiCacheのメモ。

ElastiCacheRedisがMulti-AZ対応したので、Multi-AZ構成でクラスタを作成していたのですが、 なんとParameterGroupでappendonly yesにしたgroupを設定するとエラーとなってしまうのです!ドン!(多分だけど。。。)


Cannot have redis persistence(appendonly) and Multi-AZ both on

ちなみにこんなエラー。

どうやら同じエラーになった方がいたようで、こちらの記事を参考にさせて頂きました。 [AWS]マルチAZなElastiCache Redisの永続性についてメモ

実は今年の一月ぐらいに他のインスタンスで同じMulti-AZ構成にしたクラスタがあったのですが、そちらはなんとAOFの設定が出来ているのです!(さらに深まる謎)

結局のところ

埒が明かないので、AOFの設定無しのParameterGroupを設定したところ、クラスタの作成が問題なく出来ました。 参考にさせて頂いた記事にありましたが、Multi-AZではインスタンスが死んだら、そのインスタンスは破棄するので、AOFを残す意味がないから?という仮説を立てられておりました。 自分もその意見には納得です。

だれかご存知の方いますかーーーーー?

追記 2015/02/18 この件についてAWSに問い合わせしたところ早速回答がきました! Multi-AZ構成でAOF設定は出来ない仕様 さらに英語のみですが、ドキュメントに記載されているとのこと。  - Best Practices for Implementing Amazon ElastiCache / Mitigating Out-of-Disk-Space Issues When Using AOF  http://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/BestPractices.html#BestPractices.FaultTolerance

Enabling Redis Multi-AZ as a Better Approach to Fault Tolerance  If you are enabling AOF to protect against data loss, consider using a replication group with Multi-AZ enabled instead of AOF. In any Redis replication group, if a replica fails, it is automatically replaced and synchronized with the primary cluster. If Multi-AZ is enabled on a Redis replication group and the primary fails, it fails over to a read replica. This functionality is much faster than rebuilding the primary from an AOF file. For maximum fault tolerance, even against a hardware fault in an underlying physical server, launch your primary and replica clusters in different Availability Zones. Because there is no need for AOF in this scenario, you do not risk running out of disk space. AOF ですがローカルディスクに書き込みを行う都合上、物理的な障害には対応できません。また、ディスクに書き込みを行う都合上、更新が非常に多いとローカルディスクを使い切り書き込みができなくなる事例も報告されております。この影響を回避するため、ベストプラクティスとしては AOF ではなく Multi-AZ 構成によって耐障害性を考慮頂くことをお勧めしております。 Multi-AZ 構成により十分な耐障害性が確保できることから、ElastiCache の仕様としては AOF との併用ができないような仕様となっております。

やはりMulti-AZになったことでAOFファイルつくる必要がないという判断のようです。

早急に連絡くれたサポートに感謝!

【AWS】S3のrdbファイルからElastiCache Redisを作成する時の些細な注意点

Redis S3にアップしたrdbファイルからElastiCacheを作成することってよくありますよね。ありますよね。

僕は今回初めて対応しましたが、たった些細な設定が漏れていた為、大ハマりしました。お世話になりました。

ドキュメント

バックアップと復元の管理 (Redis)

RDB ファイルへの読み込みアクセスを ElastiCache に許可 次のステップでは、Amazon S3 にコピーしておいたスナップショットファイルへの読み込みアクセスを ElastiCache に許可します。 Amazon S3 にコピーしておいたスナップショットへの読み込みアクセスを ElastiCache に許可するには AWS Management Console にサインインして Amazon S3 コンソールを開きます(https://console.aws.amazon.com/s3)。 [All Buckets] をクリックし、RDB ファイルを含む Amazon S3 バケットの名前をクリックします。 RDB ファイルを含むフォルダの名前をクリックします。 RDB ファイルの名前をクリックし、[Actions] ドロップダウンメニューをクリックして [Properties] を選択します。 [Permissions] をクリックして [Add more permissions] をクリックします。 [Grantee] ボックスに、E メールアドレスaws-scs-s3-readonly@amazon.comを入力します。 [Open/Download] をクリックし、[Save] をクリックします。 Note aws-scs-s3-readonly@amazon.com アカウントは、Amazon S3 から Redis スナップショットデータをアップロードするお客様だけが使用します。

aws-scs-s3-readonly@amazon.comを許可

はい。これを許可してなかったので、No permission to access S3ってエラーになってましたとさ。

ドキュメント大事。

(ってかこれハマる人結構いるんじゃね)

GOTRACEBACKのメモ

182b65b9 539c 4d24 9b1f ee4a1a8756f1

GOTRACEBACKという環境変数があり、それを設定することによって、go runしたときのプログラムのトレースバックが変わるようです。

The GOTRACEBACK variable controls the amount of output generated when a Go program fails due to an unrecovered panic or an unexpected runtime condition. By default, a failure prints a stack trace for every extant goroutine, eliding functions internal to the run-time system, and then exits with exit code 2. If GOTRACEBACK=0, the per-goroutine stack traces are omitted entirely. If GOTRACEBACK=1, the default behavior is used. If GOTRACEBACK=2, the per-goroutine stack traces include run-time functions. If GOTRACEBACK=crash, the per-goroutine stack traces include run-time functions, and if possible the program crashes in an operating-specific manner instead of exiting. For example, on Unix systems, the program raises SIGABRT to trigger a core dump.

Google先生で翻訳したところ、

GOTRACEBACK = 0は、単位のゴルーチンスタックトレースが完全に省略している場合は終了コード2で終了します。

GOTRACEBACK = 1の場合、デフォルトの動作が使用されます。

GOTRACEBACK = 2場合は、単位のゴルーチンスタックトレースは、実行時の機能が含まれます。

以下のサンプルコードで試してみました。


package main


import (
        "log"
        "runtime"
)

func main(){
        log.Println(runtime.NumGoroutine()) //動いているゴルーチンの数
        select{}
}

GOTRACEBACK=0


[root@localhost vagrant]# GOTRACEBACK=0 go run sample.go
2015/02/12 13:39:00 4
fatal error: all goroutines are asleep - deadlock!
exit status 2

終了ステータスコードとエラーメッセージが出力されます。

GOTRACEBACK=1


[root@localhost vagrant]# GOTRACEBACK=1 go run sample.go
2015/02/12 13:40:02 4
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select (no cases)]:
main.main()
    /vagrant/sample.go:11 +0xd5
exit status 2

終了ステータスコードとどの行でエラーになったか出力されます。

GOTRACEBACK=2


[root@localhost vagrant]# GOTRACEBACK=2 go run sample.go
2015/02/12 13:40:17 4
fatal error: all goroutines are asleep - deadlock!

runtime stack:
runtime.throw(0x54f7e3)
    /usr/local/go/src/runtime/panic.go:491 +0xad fp=0x7fff416d3b58 sp=0x7fff416d3b28
checkdead()
    /usr/local/go/src/runtime/proc.c:2854 +0x1f8 fp=0x7fff416d3ba8 sp=0x7fff416d3b58
mput(0x551e40)
    /usr/local/go/src/runtime/proc.c:3175 +0x47 fp=0x7fff416d3bb0 sp=0x7fff416d3ba8
stopm()
    /usr/local/go/src/runtime/proc.c:1176 +0xea fp=0x7fff416d3bd0 sp=0x7fff416d3bb0
findrunnable(0xc208012000)
    /usr/local/go/src/runtime/proc.c:1487 +0x562 fp=0x7fff416d3c08 sp=0x7fff416d3bd0
schedule()
    /usr/local/go/src/runtime/proc.c:1575 +0x151 fp=0x7fff416d3c38 sp=0x7fff416d3c08
runtime.park_m(0xc2080006c0)
    /usr/local/go/src/runtime/proc.c:1654 +0x113 fp=0x7fff416d3c60 sp=0x7fff416d3c38
runtime.mcall(0x42d204)
    /usr/local/go/src/runtime/asm_amd64.s:186 +0x5a fp=0x7fff416d3c70 sp=0x7fff416d3c60

goroutine 1 [select (no cases)]:
runtime.gopark(0x0, 0x0, 0x4ef830, 0x11)
    /usr/local/go/src/runtime/proc.go:130 +0x105 fp=0xc20802df08 sp=0xc20802ded8
runtime.block()
    /usr/local/go/src/runtime/select.go:176 +0x46 fp=0xc20802df30 sp=0xc20802df08
main.main()
    /vagrant/sample.go:11 +0xd5 fp=0xc20802df98 sp=0xc20802df30
runtime.main()
    /usr/local/go/src/runtime/proc.go:63 +0xf3 fp=0xc20802dfe0 sp=0xc20802df98
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc20802dfe8 sp=0xc20802dfe0

goroutine 2 [force gc (idle)]:
runtime.gopark(0x42edb0, 0x5516e0, 0x4e49d0, 0xf)
    /usr/local/go/src/runtime/proc.go:130 +0x105 fp=0xc20801a798 sp=0xc20801a768
runtime.goparkunlock(0x5516e0, 0x4e49d0, 0xf)
    /usr/local/go/src/runtime/proc.go:136 +0x48 fp=0xc20801a7c0 sp=0xc20801a798
runtime.forcegchelper()
    /usr/local/go/src/runtime/proc.go:99 +0xce fp=0xc20801a7e0 sp=0xc20801a7c0
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc20801a7e8 sp=0xc20801a7e0
created by runtime.init·4
    /usr/local/go/src/runtime/proc.go:87 +0x25

goroutine 3 [GC sweep wait]:
runtime.gopark(0x42edb0, 0x558978, 0x4e21f0, 0xd)
    /usr/local/go/src/runtime/proc.go:130 +0x105 fp=0xc20801df98 sp=0xc20801df68
runtime.goparkunlock(0x558978, 0x4e21f0, 0xd)
    /usr/local/go/src/runtime/proc.go:136 +0x48 fp=0xc20801dfc0 sp=0xc20801df98
runtime.bgsweep()
    /usr/local/go/src/runtime/mgc0.go:98 +0xbc fp=0xc20801dfe0 sp=0xc20801dfc0
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc20801dfe8 sp=0xc20801dfe0
created by gc
    /usr/local/go/src/runtime/mgc0.c:1383

goroutine 4 [finalizer wait]:
runtime.gopark(0x42edb0, 0x558970, 0x4e4550, 0xe)
    /usr/local/go/src/runtime/proc.go:130 +0x105 fp=0xc208019730 sp=0xc208019700
runtime.goparkunlock(0x558970, 0x4e4550, 0xe)
    /usr/local/go/src/runtime/proc.go:136 +0x48 fp=0xc208019758 sp=0xc208019730
runtime.runfinq()
    /usr/local/go/src/runtime/malloc.go:727 +0xba fp=0xc2080197e0 sp=0xc208019758
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc2080197e8 sp=0xc2080197e0
created by runtime.createfing
    /usr/local/go/src/runtime/malloc.go:707 +0x5e
exit status 2

長いです。終了ステータスコードと他に動いてるゴルーチンが出力されます。

こう思った

GOTRACEBACK=2で他のゴルーチンがわかるのは便利かも。 なぜall goroutines are asleep - deadlock!となるのかは謎。。。