- 投稿日:2019-05-12T22:34:20+09:00
AWS SESでメールを送信 ~golang編~
1 AWS SDK Goをインストール
go get -u github.com/aws/aws-sdk-go/...2. AWSのアクセスキー、シークレットアクセスキーを取得
参考
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_access-keys.html3. クレデンシャル情報をセット
cd アプリケーションディレクトリ vi .aws/credentials [default] aws_access_key_id = ******* aws_secret_access_key = **************4. 実行ファイルを作成
$ vi test-send.go package main import ( "fmt" //go get -u github.com/aws/aws-sdk-go "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ses" "github.com/aws/aws-sdk-go/aws/awserr" ) const ( // Replace sender@example.com with your "From" address. // This address must be verified with Amazon SES. Sender = "sender@example.com" // Replace recipient@example.com with a "To" address. If your account // is still in the sandbox, this address must be verified. Recipient = "recipient@example.com" // Specify a configuration set. To use a configuration // set, comment the next line and line 92. //ConfigurationSet = "ConfigSet" // The subject line for the email. Subject = "Amazon SES Test (AWS SDK for Go)" // The HTML body for the email. HtmlBody = "<h1>Amazon SES Test Email (AWS SDK for Go)</h1><p>This email was sent with " + "<a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the " + "<a href='https://aws.amazon.com/sdk-for-go/'>AWS SDK for Go</a>.</p>" //The email body for recipients with non-HTML email clients. TextBody = "This email was sent with Amazon SES using the AWS SDK for Go." // The character encoding for the email. CharSet = "UTF-8" ) func main() { // Create a new session in the us-west-2 region. // Replace us-west-2 with the AWS Region you're using for Amazon SES. sess, err := session.NewSession(&aws.Config{ Region:aws.String("us-west-2")}, ) // Create an SES session. svc := ses.New(sess) // Assemble the email. input := &ses.SendEmailInput{ Destination: &ses.Destination{ CcAddresses: []*string{ }, ToAddresses: []*string{ aws.String(Recipient), }, }, Message: &ses.Message{ Body: &ses.Body{ Html: &ses.Content{ Charset: aws.String(CharSet), Data: aws.String(HtmlBody), }, Text: &ses.Content{ Charset: aws.String(CharSet), Data: aws.String(TextBody), }, }, Subject: &ses.Content{ Charset: aws.String(CharSet), Data: aws.String(Subject), }, }, Source: aws.String(Sender), // Uncomment to use a configuration set //ConfigurationSetName: aws.String(ConfigurationSet), } // Attempt to send the email. result, err := svc.SendEmail(input) // Display error messages if they occur. if err != nil { if aerr, ok := err.(awserr.Error); ok { switch aerr.Code() { case ses.ErrCodeMessageRejected: fmt.Println(ses.ErrCodeMessageRejected, aerr.Error()) case ses.ErrCodeMailFromDomainNotVerifiedException: fmt.Println(ses.ErrCodeMailFromDomainNotVerifiedException, aerr.Error()) case ses.ErrCodeConfigurationSetDoesNotExistException: fmt.Println(ses.ErrCodeConfigurationSetDoesNotExistException, aerr.Error()) default: fmt.Println(aerr.Error()) } } else { // Print the error, cast err to awserr.Error to get the Code and // Message from an error. fmt.Println(err.Error()) } return } fmt.Println("Email Sent to address: " + Recipient) fmt.Println(result) }5 実行ファイルのビルド、実行
$ go build -o test-send test-send.go $ ./test-go Email Sent to address: ***** { MessageId: "***********" }参考
https://docs.aws.amazon.com/ja_jp/sdk-for-go/v1/developer-guide/setting-up.html
- 投稿日:2019-05-12T18:06:42+09:00
GCP+GKE+GOのアーキテクチャを理解する
はじめに
この記事は、GCPとGKEとGOを用いたマイクロサービスの開発初心者が、
個々の技術単体ではなくそれらを組み合わせたアーキテクチャを理解することを目的としています。私自身がGCPとGKEとGOを用いたマイクロサービスの開発方法を勉強し始めている中で、
「GCPもGKEもGOも概要はなんとなく理解した。しかし合わせて考えるとまだつながっていないな」
という状態になっていました。
そこで、GCP+GKE+GOのアーキテクチャを理解しようと思います。環境項築
「Googleのクイックスタートガイド」に沿って環境を構築します。
GCPでGKEを有効にし、クラスタを作成してそれにノード3つ用意してGOアプリをデプロイする・・・という流れになっていますね。
特に躓いた箇所は無かったです。動作確認
1.GCPのGoogle Cloud Shellで以下のコマンドを実行
・hello-server Service を検査
kubectl get service hello-server【結果】
?????@ cloudshell:~ (level-datum-236008)$ kubectl get service hello-server
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-server LoadBalancer 10.23.248.52 12.345.678.99 8080:30727/TCP 38d2.ウェブブラウザで、「1」のEXTERNAL-IP(公開されているポートを指定した外部IPアドレス)を使用し、
GOアプリケーションを表示
http://[EXTERNAL_IP]:8080
例 http://12.345.678.99:8080【結果】
Hello, world!
Version: 1.0.0
Hostname: hello-server-0123456789-01234これで、コンテナ化されたGOアプリが
GKEにデプロイされたことを確認できました。GCP+GKE+GOのアーキテクチャ
・GCP(Google Cloud Platform)
Google がクラウド上で提供するサービス群の総称。・GKE(Google Kubernetes Engine)
GCP上でKubernetesを簡単に使うことができるサービス。・Kubernetes
コンテナ(アプリケーション)で構成されたサービスのデプロイやスケーリング、
構成の管理をしてくれるGoogle製のオープンソース。・GO(GoLang)
Googleが開発したオープンソースプロジェクトのプログラミング言語。・クラスター
ノードを構成しているもの。・ノード
コンテナが動くサーバー。・コンテナ
作成したアプリケーションを動かすもの。おわりに
個々の技術を組み合わせたアーキテクチャを理解することができました。
わけあっていきなりGCP+GKE+GOの組み合わせについて投稿してしまいましたので、
次回はGCPについて投稿しようと思います。
- 投稿日:2019-05-12T12:51:50+09:00
GO111MODULE=onなプロジェクトでDockerのビルド時間を短くした工夫
はじめに
Go1.11から採用のGo Modules(vgo)なプロジェクトにて、Dockerのビルドが遅かったのをキャッシュ利用で一応解決できた話。
順を追って
はじめの状態
下記のような構成
. ├── Dockerfile ├── go.mod ├── go.sum └── main.goDockerfile一部抜粋COPY . ./ RUN CGO_ENABLED=0 GOOS=linux go build -o binary # 遅い!Goコードの中では大きなOSSプロジェクトライブラリを利用しているので本コードを改修するたびにDockerビルドにて対象パッケージをダウンロードして遅い!
次にvendorをレポジトリに入れてみた
下記のコマンドでパッケージたちをvendorとしてレポジトリに組み込んでみました。
$ go mod vendor $ tree . ├── Dockerfile ├── go.mod ├── go.sum ├── main.go └── vendor ├── package1 ├── package2Dockerfile一部抜粋COPY . ./ # ここの通信コストが大きくなってしまった RUN CGO_ENABLED=0 GOOS=linux go build -mod=vendor -o binary # ここは速くなったけどこれでパッケージを毎回ダウンロードしないのでビルド時間はたしかに短くなった。
しかしレポジトリが巨大になってしまいました。
そして、このプロジェクトではDocker-Machineのリモート接続でDockerを扱っているのでローカルからリモートへの通信コストがvendorのせいでエグいことになってしまいました。
(そもそも.dockerignoreもまともに記載していなかったのでこれはこれで対応。)解決策 パッケージダウンロードなイメージレイヤを設けてキャッシュ利用
vendorは削除しました。
他言語のプロジェクトと同様な戦略でパッケージに関わる部分を先にダウンロードするイメージレイヤを作ることでキャッシュを活用することにしました。Dockerfile一部抜粋ENV GO111MODULE="on" # Go Modulesを使うと明言する COPY go.mod go.sum ./ # パッケージ用メタファイルだけコピー RUN go mod download # メタファイルに変更がなければキャッシュ利用できる COPY . ./ RUN CGO_ENABLED=0 GOOS=linux go build -o binary
go.modに修正が入らない限りRUN go mod downloadはキャッシュが利用されるので、一番はじめのビルド時間の遅さは解決できました。
また、新しいパッケージを利用しはじめてそれがgo.modに反映されていなくてもgo buildが代わりにダウンロードするので問題なくイメージがビルドされます。最後にDockerfileの全体感
Dockerfileの全体としては以下のような感じにしています。
マルチステージングの機能を利用して最終的にできあがるイメージは最小になるよう努めています。DockerfileFROM golang:1.12.5-stretch as builder WORKDIR /workdir ENV GO111MODULE="on" COPY go.mod go.sum ./ RUN go mod download COPY . ./ RUN CGO_ENABLED=0 GOOS=linux go build -o binary FROM alpine:3.8 WORKDIR /root/ # Multi stage build function of Docker COPY --from=builder /workdir/binary . CMD ["./binary"]
- 投稿日:2019-05-12T07:25:54+09:00
Nuxt と Go で Software Architecture の実験 ~1日目~
はじめに
Software Architecture について学ぼうとした矢先に、Nuxt.js(Vue.js)とGoでSPA + API(レイヤードアーキテクチャ)でチャットアプリを実装してみたという記事をみつけ、とても勉強になったため、1から自分でも実装してみることにしました。
これはその日記です。1日目はNuxtとGoで簡易的なstatic serverを作成しました。
NuxtのSkeletonの作成
client folerの配下に、nuxt_go_template folderを作成し、その中でnuxtをインストールします。
$ yarn create nuxt-app <project-name>GoでSkeletonサーバ (static server) の作成
まずは、依存関係管理ツールのdepをインストールします(詳しくはこの記事を参考にしてください。)。
$ cd server $ dep init次に、server folder下にインフラ層を担当するinfra folderを作成し、さらにその配下に、routing技術を担当するrouter folderを作成します。
router folderの配下に、router.goを設置し、gorilla muxと呼ばれるhttp request multiplexerのpackageを利用してルーターをインスタンス化します。
router.gopackage router import ( "github.com/gorilla/mux" ) // Router is the gorilla router for API. var Router *mux.Router func init() { // Instantiation for gorilla Router. r := mux.NewRouter() Router = r }server配下のmain.goには静的ファイルをホスティングするサーバーを構築します。
main.gopackage main import ( "net/http" "github.com/hideUW/nuxt_go_template/server/infra/router" ) func main() { // rootに対してnuxtのindex.htmlを返すように設定する。(ServeFile) entrypoint := "../client/nuxt_go_template/dist/index.html" router.Router.Path("/").HandlerFunc(ServeStaticFile(entrypoint)) // 静的ファイルの提供 // $PROROOT/_nuxt/ 以下のファイルに http://localhost:8080/_nuxt/~.html でアクセスできるように設定する。(FileServer) router.Router.PathPrefix("/_nuxt/").Handler( http.StripPrefix("/_nuxt/", http.FileServer(http.Dir("../client/nuxt_go_template/dist/_nuxt/")))) // エラーハンドリング if err := http.ListenAndServe(":8080", router.Router); err != nil { panic(err.Error()) } } // 静的ファイルをホスティングする。 func ServeStaticFile(entrypoint string) func(w http.ResponseWriter, r *http.Request) { fn := func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, entrypoint) } return http.HandlerFunc(fn) }Goのhttp.Handlerやhttp.HandlerFuncをちゃんと理解するという記事を参考にすると以下のように動作している事がわかります。
http.Hundler
http.Handlerは
ServeHTTPというメソッドを定義しているinterfaceです。このServeHTTPメソッドはHTTPリクエストを受けてHTTPレスポンスを返します。type Handler interface { ServeHTTP(ResponseWriter, *Request) }http.ServeMux のHandleメソッドの引数がこのinterface型になっています。
func (mux *ServeMux) Handle(pattern string, handler Handler) { .... }HandlerFunc
- func(ResponseWriter, *Request) を別の型として定義したものです。
- このHandlerFunc型にはServeHTTP(ResponseWriter, *Request)メソッドが実装されています。func(http.ResponseWriter, *http.Request) のシグネチャの関数を自分で定義しておき、このHandlerFuncにキャストするだけで構造体を宣言することなく http.Handler を実装することができます。
type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }func hello(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "Hello World") } func main() { mux := http.NewServeMux() mux.Handle("/hello", http.HandlerFunc(hello)) // helloをHandlerFuncにキャスト }
- helloをhttp.HandlerFuncにキャストしています。
- http.HandlerFuncは ServeHTTP を実装しているので、ServeMux.Handleの第2引数に指定することが可能です。
おわりに
以下のコマンドを実行すると、Nuxtのindex.htmlが表示されて、ローカルホストを利用したstatic severが完成している事がわかります。
$ cd server $ go run main.go明日もがんばります。

