virtualboxの共有フォルダの設定について
# ls -l /media drwxrwx--- 1 root vboxsf 0 12月 11 17:09 2016 sf_vbox_share
このままだと権限がないので、 rootかvboxsfグループのユーザーしかアクセスできないため、 既存ユーザーをvboxsfグループに追加しておく。有効にするため一度再起動したら、 共有できるようになっているはず。単に共有さえできればいいならここまでで設定は終わり。
# gpasswd -a vagrant vboxsf # reboot
windows10の場合はシンボリックリンクを設定が必要 everyoneを設定する
digdagServerの構築メモ
Centos7にdigdagサーバーを構築した際のメモ
久しぶりに自分のローカルPCにCentos7でサーバーを構築しようとしたが、 vagrant up しようとするとエラーが発生。。
下記組み合わせだとvagrant upがエラーする
D:\vagrant\labo>vagrant up The version of powershell currently installed on this host is less than the required minimum version. Please upgrade the installed version of powershell to the minimum required version and run the command again. Installed version: 2 Minimum required version: 3
上記エラーメッセージからPowerShellのバージョンを2系から3系以上に変更したが 次にWMF5.0(Windows Management Framework 5.0)をインストールする必要もあった。 詳細は以下のリンクから確認できる。
WMFのインストールが必要
digdagServerを構築する際にpostgreSQLを
- postgres9.6のインストールは以下のURLを参考にしてインストール
- CREATE EXTENSIONしないとエラーになるので注意
digdag_db=> CREATE EXTENSION "uuid-ossp"; ERROR: could not open extension control file "/usr/pgsql-9.6/share/extension/uuid-ossp.control": No such file or directory ## 拡張モジュールのインストールが必要 # yum -y install postgresql96-contrib.x86_64 ## postgresユーザーでログインして psql -U postgres -h 127.0.0.1 -d digdag_db CREATE EXTENSION "uuid-ossp";
postgre9.6 install
- https://qiita.com/purini-to/items/702565733a03c202d98e
- https://qiita.com/uhooi/items/44ed9370740c7521dce4
digdag/embulkのインストール
- java1.8がインストールされていることが前提
$ java -version openjdk version "1.8.0_151" OpenJDK Runtime Environment (build 1.8.0_151-b12) OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode) # インストールされていなければいれる $ yum -y install java-1.8.0-openjdk
# Embulkの導入 $ curl --create-dirs -o ~/bin/embulk -L https://dl.embulk.org/embulk-latest.jar $ chmod +x ~/bin/embulk # Digdagの導入 $ curl --create-dirs -o ~/bin/digdag -L "https://dl.digdag.io/digdag-latest" $ chmod +x ~/bin/digdag # OSX環境の場合は次のように.bash_profileを編集する $ cat << 'EOF' >> ~/.bash_profile # User specfic environment and startup programs export PATH=$PATH:$HOME/bin EOF $ source ~/.bash_profile
digdagはまりポイント
https://qiita.com/toyama0919/items/142d290c8dcb2c86851c
# vi /usr/lib/systemd/system/digdag.service [Unit] Description=digdag [Service] Type=simple PIDFile=/run/digdag.pid ExecStart="/usr/local/bin/digdag server -b 0.0.0.0 --config /opt/digdag/server.properties -O /opt/digdag/logs/tasklogs -A /opt/digdag/logs/accesslogs -L /opt/digdag/logs/server.log" User=root Group=root WorkingDirectory=/opt/digdag Restart=always RestartSec=5 KillMode=process TimeoutStopSec=1200 SyslogIdentifier=digdag [Install] WantedBy=multi-user.target
ExecStartは別ファイルにしないとエラーになるので注意
シェルは別ファイルにしないとエラーする
[Unit] Description=digdag [Service] Type=simple PIDFile=/run/digdag.pid ExecStart=/opt/digdag/start.sh User=root Group=root [Install] WantedBy=multi-user.target
https://qiita.com/cmwig65/items/3386a061aeb3d2f81b81 https://qiita.com/toyama0919/items/142d290c8dcb2c86851c https://qiita.com/bwtakacy/items/ec3151644512ca65f6b6
Go言語でJSONのデコード処理を検証してみた
GOでJSONをデコードする際の方法
GoでJSONを処理する最も一般的な方法
package main import "encoding/json" type Message struct { Name string Body string Time int64 } func main() { b := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`) var m Message json.Unmarshal(b,&m) println(m.Name) // Alice println(m.Body) // Hello println(m.Time) // 1294706395881547000 }
- bとして定義されたJSONをjson.Unmarshalでデコード
- ポイントは、デコード結果を受け取るために構造体を定義している点
- 対象のJSONデータの構造を確認しつつ、実装時に構造体の定義を決める必要がある
複雑な構造のJSON処理で直面する問題
{ "took": 1, "timed_out": false, "_shards":{ "total" : 1, "successful" : 1, "failed" : 0 }, "hits":{ "total" : 1, "max_score": 1.3862944, "hits" : [ { "_index" : "twitter", "_type" : "tweet", "_id" : "0", "_score": 1.3862944, "_source" : { "user" : "kimchy", "message": "trying out Elasticsearch", "date" : "2009-11-15T14:12:12", "likes" : 0 } } ] } }
上記のJSONデータをデコードする処理を、単純にコーディングすると
package main import ( "encoding/json" "net/http" ) type ResultShards struct { Total int `json:"total"` Successful int `json:"successful"` Failed int `json:"failed"` } type ResultHitSource struct { User string `json:"user"` Message string `json:"message"` Date string `json:"date"` Likes int `json:"likes"` } type ResultHit struct { Index string `json:"_index"` Type string `json:"_type"` Id string `json:"_id"` Score float32 `json:"_score"` Source ResultHitSource `json:"_source"` } type ResultHits struct { Total int `json:"total"` MaxScore float32 `json:"max_score"` Hits []ResultHit `json:"hits"` } type Result struct { Took int `json:"took"` TimedOut bool `json:"timed_out"` Shards ResultShards `json:"_shards"` Hits ResultHits `json:"hits"` } func main() { resp, err := http.Get("http://127.0.0.1:9200/_search") if err != nil { panic(err) } defer resp.Body.Close() var result Result if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { panic(err) } println(result.Hits.Total) }
デコード結果格納までのコードの見通しをよくする
- 上記の例では、Goの平易な言語仕様しか用いていないために、見通しの悪い実装となっているのでリファクタリングする
- Goは構造体をネスト定義することが可能
- さらにネスト定義する構造体には型名が必要ないので、これも削除する
package main import ( "encoding/json" "net/http" ) type Result struct { Took int `json:"took"` TimedOut bool `json:"timed_out"` Shards struct { Total int `json:"total"` Successful int `json:"successful"` Failed int `json:"failed"` } `json:"_shards"` Hits struct { Total int `json:"total"` MaxScore float32 `json:"max_score"` Hits []struct { Index string `json:"_index"` Type string `json:"_type"` Id string `json:"_id"` Score float32 `json:"_score"` Source struct { User string `json:"user"` Message string `json:"message"` Date string `json:"date"` Likes int `json:"likes"` } `json:"_source"` } `json:"hits"` } `json:"hits"` } func main() { resp, err := http.Get("http://127.0.0.1:9200/_search") if err != nil { panic(err) } defer resp.Body.Close() var result Result if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { panic(err) } println(result.Hits.Total) }
- ネストに定義することで、本来のJSONの出力形式に見た目が近くなり、かつコード量も減る
発展的な実装テクニック
- 出力されるJSONの形式を完全に構造体でカバーする必要がなく、参照が必要なオブジェクトだけを構造体の定義でおさえるようにしても、デコード処理に支障は出ない
- ヒットしたドキュメントのリストのみを参照した場合は以下のように実装することができる
package main import ( "encoding/json" "net/http" ) func main() { resp, err := http.Get("http://127.0.0.1:9200/_search") if err != nil { panic(err) } defer resp.Body.Close() var result struct { Hits struct { Hits []struct { Source struct { Title string `json:"title"` Description string `json:"description"` ImageUrl string `json:"image_url"` Url string `json:"detail_url"` } `json:"_source"` } `json:"hits"` } `json:"hits"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { panic(err) } for _, hit := range result.Hits.Hits { println(hit.Source.Title) println(hit.Source.Description) println(hit.Source.ImageUrl) println(hit.Source.Url) } }
- JSONデータに含まれるオブジェクトには、エラー情報のように「もしかしたら返却されるかもしれない」オブジェクトというものもあるので、こういった場合は対応するフィールドをポインタ型で用意するう
構造体定義の自動生成
- デコード結果を受け取る構造体の定義は、JSONデータから自動生成するWebアプリが公開されている
interfaceを利用して局所的に参照する
- 必要なオブジェクトをピンポイントに取り出す
- 各オブジェクトへのアクセスには型アサートを利用する
package main import ( "encoding/json" "net/http" ) func main() { resp, err := http.Get("http://127.0.0.1:9200/_search") if err != nil { panic(err) } defer resp.Body.Close() var result interface{} decoder := json.NewDecoder(resp.Body) if err := decoder.Decode(&result); err != nil { panic(err) } n, _ := result.(map[string]interface{})["hits"].(map[string]interface{})["total"].(float64) println(int(n)) // 359 // (int にキャストしなかった場合:+3.590000e+002) }
- JSONデータ内に検索結果として格納されている、ヒット件数の値を取り出しています
- 整数型はすべてfloat64として格納されているので、取得後にintにキャストする操作を行っている
Jasonライブラリ
- JasonというGitHub上で個人開発されているパッケージ
- GoのJSONライブラリの中では比較的なメジャーなもの
- 入れ子のオブジェクトを辿るためのインタフェイスも提供されていて、Jason を利用しない場合、型アサートを繰り返し記述することになりますが、これを簡略化して実装できる
package main import ( "github.com/antonholmquist/jason" "net/http" ) func main() { resp, err := http.Get("http://127.0.0.1:9200/_search") if err != nil { panic(err) } defer resp.Body.Close() v, err := jason.NewObjectFromReader(resp.Body) if err != nil { panic(err) } n, err := v.GetInt64("hits", "total") if err != nil { panic(err) } println(n) }
参考URL
- https://qiita.com/msh5/items/dc524e38073ed8e3831b
- https://stackoverflow.com/questions/21197239/decoding-json-in-golang-using-json-unmarshal-vs-json-newdecoder-decode
- http://kudohamu.hatenablog.com/entry/2014/11/05/165133
- https://qiita.com/nayuneko/items/2ec20ba69804e8bf7ca3
DecoderとUnmarshalの使い分け
- データがio.Readerストリームからのものである場合、またはデータストリームから複数の値をデコードする必要がある場合は、json.Decoderを使用します。
- すでにJSONデータがメモリにある場合は、json.Unmarshalを使用します。
Use json.Decoder if your data is coming from an io.Reader stream, or you need to decode multiple values from a stream of data.
Use json.Unmarshal if you already have the JSON data in memory.
Amazon S3に保存されているバケット毎のオブジェクト容量、オブジェクト数を知る方法は大きく分けて下記の2つの方法がある
AWS SAMを利用してGolangなLambdaをデプロイする
AWS CLIでデプロイ(Windows環境)
$ GOOS=linux go build -o main $ zip deployment.zip main
$ aws lambda create-function \ --region us-west-2 \ --function-name HelloFunction \ --zip-file fileb://./deployment.zip \ --runtime go1.x \ --tracing-config Mode=Active \ --role arn:aws:iam::account_id:role/role_name \ --handler main
aws lambda create-functionのオプション
https://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html
depoly方法
https://github.com/aws/aws-lambda-go https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/deploying-lambda-apps.html
AWS SAMを利用してLambdaをデプロイする(Mac環境)
- template.ymlのroleは指定する
テンプレート
$ cat template.yml AWSTemplateFormatVersion: "2010-09-09" Transform: 'AWS::Serverless-2016-10-31' Resources: App: Type: 'AWS::Serverless::Function' Properties: Handler: lambda-go-sample # ファイル名 Runtime: go1.x CodeUri: build # ビルドファイルの設置ディレクトリを設定 Role: arn:aws:iam::account_id:role/role_name # Roleを設定する Timeout: 1
デプロイ
$ GOARCH=amd64 GOOS=linux go build -o build/lambda-go-sample $ aws cloudformation package \ --template-file template.yml \ --s3-bucket <bucket-name> \ --s3-prefix lambda-go-sample \ --output-template-file .template.yml $ aws cloudformation deploy \ --template-file .template.yml \ --stack-name lambda-go-sample \ --capabilities CAPABILITY_IAM
実行結果
$ aws cloudformation describe-stack-resources --stack-name lambda-go-sample
実行
$ aws lambda invoke --function-name lambda-go-sample-App-xxxx --payload '"Lambda"' out.txt
Docker compose
- npmコマンドが使用できる必要あり
- docker-composeコマンドが使用できる必要あり
docker-compose.ymlファイルとは
docker-compose.yml
ファイルは以下のようにyaml形式でDockerコンテナに関する起動オプション(buildオプションも含まれることもある)を記述したファイル
docker-compose.ymlサンプル
web: build: . ports: - "5000:5000" volumes: - .:/code links: - redis redis: image: redis # yaml の記載方法については下記を参照 # https://docs.docker.com/compose/compose-file/
Docker Compose概要
Docker composeとは複数のコンテナから成るサービスを構築・実行する手順を自動的にし、管理を用意にする機能 Docker composeでは、composeファイルを用意してコマンドを1回実行することで、そのファイルから設定を読み込んで全てのコンテナサービスを起動することができる
Docker Composeを使うまでの主なステップ
- それぞれのコンテナのDockerfieを作成する(既にあるイメージを使う場合は不要)
- docker-compose.ymlを作成し、それぞれ独立したコンテナの起動定義を行う(場合によっては構築定義も含まれる)
docker-compose up
コマンドを実行してdocker-compose.ymlで定義したコンテナを開始する
動作確認を行う環境について
(Working dir) +- docker-compose.yml +- app-server/ +- Dockerfile +- src/ +- app.js
Docker composeを使用した簡単なサンプル
Docker composeを使用した複数コンテナでひとつのサービスを作成するサンプルを実施してみる redisを使用してアクセス数をカウントする簡単なアプリケーションを作成してみる
application側の作成
アプリケーション側はnode.jsを使用する nodeの公式レポジトリを使用するための、Dockerfileは以下のように簡単なもの
app-server/Dockerfile
FROM node:5 RUN npm -g install redis ENV NODE_APTH /usr/local/lib/mode_modules ENTRYPOINT ["node", "app.js"]
npm
はnode.jsのpackageを管理するためのツール
ENTRYPOINT ["node", "app.js"]は nodeコマンドの引数にapp.jsを渡している
Redis側の作成
Redis側のコンテナの作成を行うが、公式のRedisイメージを使用するため、Dockerfileは不要
Docker composeファイルの作成
アプリ側のnodeのアプリケーションとredis側のアプリケーションをbuild,runするための定義をdocker-coposeファイルに記載する
docker-compose.yml
nodeapp: build: "./app-server" container_name: "nodeapp" working_dir: "/usr/src/app" ports: - "10080:10080" volumes: - "$PWD/app-server/src:/usr/src/app" links: - "noderedis" noderedis: image: "redis:3" container_name: "noderedis"
nodeのアプリケーションが乗るコンテナは、Dockerfileからイメージをbuildしてそこからnodeappという名前のコンテナを起動する redisが乗るコンテナについては、DockerfileからイメージをせずDocker hubからpullしてきた公式イメージを利用し、そこからnoderedisという名前のコンテナを起動する
docker-compose.yml
ファイルのあるディレクトリ移動し、docker-compose up
コマンドを実行する
$ docker-compose up
動作確認
$ curl http://localhost:10080 You accessed here 1 times. $ curl http://localhost:10080 You accessed here 2 times.
docker-composeコマンドで作成されるイメージ名
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE wk_nodeapp latest c36a03206379 15 minutes ago 649MB redis 3 256639e384de 8 weeks ago 99.7MB node 5 12b4a63115bc 17 months ago 648MB $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9fa382e87f70 wk_nodeapp "node app.js" 15 minutes ago Up 15 minutes 0.0.0.0:10080->10080/tcp nodeapp dba91dafd516 redis:3 "docker-entrypoint.s…" 15 minutes ago Up 15 minutes 6379/tcp noderedis
docker-compose.yml
nodeapp: # <- サービス名 build: "./app-server" # <- Dockerfile のあるファイルの場所(Dockerfile のある場所。git リポジトリのURL も指定可能) container_name: "nodeapp" # <- コンテナ名。指定しなかった場合はDocker compose で勝手に決められる working_dir: "/usr/src/app" # <- コンテナ内のワーキングディレクトリ。docker run コマンドの-w/--workdir に相当 ports: # <- Expose するポート。docker run コマンドの-p/--publish に相当 - "10080:10080" volumes: # <- Bind mount するディレクトリ。volume。docker run コマンドの-v/--volume に相当 - "$PWD/app-server/src:/usr/src/app" links: # <- 他のコンテナと接続するときのコンテナ名。docker run コマンドの--link に相当 - "noderedis" noderedis: image: "redis:3" # <- イメージIDとtag container_name: "noderedis"
参考URL https://qiita.com/TsutomuNakamura/items/7e90e5efb36601c5bc8a