追記
Logging Driver の件、追記した。
samuelkarp さん、本当に有難うございました。
tl;dr
思う所があり以下を試してみた。
- Docker をソースコードからビルドして開発中のバージョンを利用する
- Logging Driver に CloudWatch Logs が追加されそうなので試してみる
- AWS SDK for Go を少し触ってみる
Docker をソースコードからビルドして開発版を利用する手順
概要
- ビルド手順はコンテナを利用して行われる
- 最新の Docker が必要
- ビルド→バイナリ生成が完了すると bundles 以下にバイナリが生成されている
参考
- https://docs.docker.com/v1.5/contributing/devenvironment/
- http://qiita.com/Arturias/items/8c9c9321e87675c7d023
- http://tristan.lt/blog/docker-4-build-docker-from-sources/
- https://master.dockerproject.org/ → ビルド済みイメージの総本山
ビルド済みのイメージを利用しても良いかもしれない。
動作確認環境
$ cat /etc/system-release Amazon Linux AMI release 2015.09
と
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=14.04 DISTRIB_CODENAME=trusty DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS"
も一つは手元の VirtualBox 上の Ubuntu 14.04 を利用した。
最新の Docker をインストール
- Ubuntu 環境の場合
$ curl -sSL https://get.docker.com/ | sh
尚、Amazon Linux の場合には Docker 1.7.1 を利用した。
ソースコードの取得とビルド
git clone
する。
$ git clone https://github.com/docker/docker.git
$ cd docker
$ make build
開発版バイナリの作成
$ make binary
開発版のバイナリを利用
Docker サービスが動いている場合には一旦停止した上でバイナリを置き換える。
# 既存 Docker バイナリをバックアップ $ sudo mv /usr/bin/docker /usr/bin/docker.old # 最新の Docker バイナリをコピー $ sudo cp cp bundles/1.9.0-dev/binary/docker-1.9.0-dev /usr/bin/ # シンボリックリンクを /usr/bin/docker に張る $ sudo ln -s /usr/bin/docker-1.9.0-dev /usr/bin/docker
開発版バイナリで Docker を起動する
$ sudo service docker start
バージョンの確認。
$ docker version Client: Version: 1.9.0-dev API version: 1.21 Go version: go1.4.2 Git commit: ce092ed Built: Wed Sep 23 20:52:39 UTC 2015 OS/Arch: linux/amd64 Server: Version: 1.9.0-dev API version: 1.21 Go version: go1.4.2 Git commit: ce092ed Built: Wed Sep 23 20:52:39 UTC 2015 OS/Arch: linux/amd64
Logging driver for CloudWatch Logs の動作確認
トィウィッターを見ていたら...
Pull request for #Docker from my colleague Sam Karp: Log Driver for Amazon CloudWatch Logs - http://t.co/5FbeVqoV0y pic.twitter.com/0B0egPHupf
— Jeff Barr (@jeffbarr) September 19, 2015
おお、スゴイ!と思ったので試してみることにした。
注意
あくまでも開発中の機能の為、実際のリリースの際に提供される機能とは異なる場合があるのでご注意を...。お試しの際は自己責任で宜しくお願いいたします。
参考
- https://github.com/docker/docker/blob/master/docs/reference/logging/awslogs.md
- https://github.com/aws/amazon-ecs-agent/issues/200
上記 issue は Docker サービスそのもので Log Driver を利用されようとしているが、「コンテナ毎に Log Stream を指定するんだよ」とコメントされている。
必要なもの
- 開発版 Docker(1.9.0-dev)
- CloudWatch Logs にて最低でも Log Stream 作成、Log を PUT 出来る権限が適用された IAM role 又は同権限が付与された IAM User の Credential な情報(以下、ドキュメントより抜粋)
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Effect": "Allow", "Resource": "*" } ] }
また、事前に Log Group を作成しておく。
ただいま開発途上
VirtualBox 上の Ubuntu 14.04 にて以下のように実行したところ、正常にコンテナは起動してくれなかった。→ /etc/default/docker に Credential 情報を設定したら起動した。
$ export AWS_ACCESS_KEY_ID=AKXXXXXXXXXXXXXXXXXXXX $ export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX $ export AWS_REGION=ap-northeast-1 $ docker run \ --log-driver=awslogs \ --log-opt awslogs-region=ap-northeast-1 \ --log-opt awslogs-group=docker-log \ --log-opt awslogs-stream=hello-world \ hello-world
以下のようなエラー。Credential 周りの設定が影響しているのか Log Stream が作成されない。→ /etc/default/docker に Credential 情報を設定したら起動した。
Error response from daemon: Cannot start container 964c628a7c4ca1264d538b3b24640130d2c4cb7a4801116a4b761853615c2944: Failed to initialize logging driver: NoCredentialProviders: no valid providers in chain
ちょっと残念だが、環境変数に設定した Credential な情報や $HOME/.aws/credential
ファイルを見つけてくれていないのが原因のようなので...
場当たりパッチで動かしてみた→こんなパッチを当てる必要は無い
以下のようにその場しのぎで $HOME/.aws/credential
ファイルを認証情報として利用するようにしてみた。
$ git diff diff --git a/daemon/logger/awslogs/cloudwatchlogs.go b/daemon/logger/awslogs/cloudwatchlogs.go index 23fc283..272508e 100644 --- a/daemon/logger/awslogs/cloudwatchlogs.go +++ b/daemon/logger/awslogs/cloudwatchlogs.go @@ -11,6 +11,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/docker/docker/daemon/logger" @@ -85,12 +86,15 @@ func New(ctx logger.Context) (logger.Logger, error) { Region: aws.String(ctx.Config[regionKey]), }) } + + fmt.Println(config) containerStream := &logStream{ logStreamName: logStreamName, logGroupName: logGroupName, - client: cloudwatchlogs.New(config), + client: cloudwatchlogs.New(&aws.Config{Credentials: credentials.NewSharedCredentials("/home/vagrant/.aws/credentials", "default"), Region: aws.String("ap-northeast-1")}), messages: make(chan *logger.Message, 4096), } + err := containerStream.create() if err != nil { return nil, err
改めて...
$ docker run \ --log-driver=awslogs \ --log-opt awslogs-region=ap-northeast-1 \ --log-opt awslogs-group=docker-log \ --log-opt awslogs-stream=hello-world \ hello-world
以下のように出力された。
Hello from Docker. This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker Hub account: https://hub.docker.com For more examples and ideas, visit: https://docs.docker.com/userguide/
CloudWatch Logs のコンソールでもログを確認することが出来た。
IAM role を適用した EC2 の場合
ちなみに、IAM role を適用した EC2 で試したところ、上記のような場当たり対応不要で CloudWatch Logs にコンテナのログが飛んでいくのを確認している。
# Docker バージョンの確認 [ec2-user@ip-xx-x-x-xxx ~]$ docker version Client: Version: 1.9.0-dev API version: 1.21 Go version: go1.4.2 Git commit: ce092ed Built: Wed Sep 23 20:52:39 UTC 2015 OS/Arch: linux/amd64 Server: Version: 1.9.0-dev API version: 1.21 Go version: go1.4.2 Git commit: ce092ed Built: Wed Sep 23 20:52:39 UTC 2015 OS/Arch: linux/amd64 # IAM role を確認 [ec2-user@ip-xx-x-x-xxx ~]$ curl http://169.254.169.254/latest/meta-data/iam/info { "Code" : "Success", "LastUpdated" : "2015-09-25T06:17:41Z", "InstanceProfileArn" : "arn:aws:iam::1234567890123:instance-profile/ec2", "InstanceProfileId" : "AIPAIZV2B4SWPBULEILCS" } # 適用されている role のポリシーを確認 [ec2-user@ip-xx-x-x-xxx ~]$ saws No resource cache found Refreshing resources... Refreshing instance ids... You must specify a region. You can also configure your region by running "aws configure". Refreshing instance tags... You must specify a region. You can also configure your region by running "aws configure". You must specify a region. You can also configure your region by running "aws configure". Refreshing bucket names... A client error (AccessDenied) occurred when calling the ListBuckets operation: Access Denied Done refreshing Version: 0.2.1 Theme: vim saws> aws iam get-role-policy --role-name ec2 --policy-name cloudwatch-logs { "RoleName": "ec2", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*", "Effect": "Allow" } ] }, "PolicyName": "cloudwatch-logs" } saws>
コンテナを起動。
[ec2-user@ip-xx-x-x-xxx ~]$ docker run -d \ --publish 80:80 \ --log-driver=awslogs \ --log-opt awslogs-region=ap-northeast-1 \ --log-opt awslogs-group=docker-log \ --log-opt awslogs-stream=nginx nginx
アクセスしてみる。
[ec2-user@ip-xx-x-x-xxx ~]$ curl -I localhost HTTP/1.1 200 OK Server: nginx/1.9.5 Date: Fri, 25 Sep 2015 06:32:52 GMT Content-Type: text/html Content-Length: 612 Last-Modified: Tue, 22 Sep 2015 16:10:55 GMT Connection: keep-alive ETag: "56017d8f-264" Accept-Ranges: bytes
ログを確認してみると以下のように記録されている。
AWS SDK for Go の Credential 設定について
モチベーション
上記の通りコンテナから CloudWatch Logs にログが飛ばない(Log Stream が作成されない)件を調査の流れから...。
参考
- http://docs.aws.amazon.com/sdk-for-go/api/aws/credentials.html
- http://docs.aws.amazon.com/sdk-for-go/api/aws/defaults.html
よく解ってないけど書いてみた
ドキュメントの読み方すらよく理解していないけど、以下のように書いてみた。S3 のバケット一覧を出力するやつ。
package main import ( "os" "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/service/s3" ) func main() { credentialsProvider := credentials.NewChainCredentials( []credentials.Provider{ &credentials.EnvProvider{}, &credentials.SharedCredentialsProvider{Filename: "", Profile: "default"}, }) svc := s3.New(&aws.Config{Credentials: credentialsProvider, Region: aws.String(os.Getenv("AWS_REGION"))}) var params *s3.ListBucketsInput result, err := svc.ListBuckets(params) if err != nil { log.Println(err.Error()) return } log.Println(result) }
とりあえず実行。
$ go run s3-test.go 2015/09/25 05:57:15 { Buckets: [ { CreationDate: 2015-01-17 06:46:12 +0000 UTC, Name: "foo" }, { CreationDate: 2015-07-13 12:44:01 +0000 UTC, Name: "bar" }, (snip) { CreationDate: 2014-03-30 16:53:07 +0000 UTC, Name: "baz" } ], Owner: { DisplayName: "hagepika", ID: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } }
おお。
ポイント
以下の部分がポイントかと。
credentialsProvider := credentials.NewChainCredentials( []credentials.Provider{ &credentials.EnvProvider{}, &credentials.SharedCredentialsProvider{Filename: "", Profile: "default"}, })
credentials.EnvProvider{}
と &credentials.SharedCredentialsProvider{Filename: "", Profile: "default"}
で環境変数又は $HOME/.aws/credentials
の default
に定義されている情報を利用出来るようにした credentialsProvider
を定義している。
そして S3 への接続(クライアントの作成)時に以下のように Credentials:
に credentialsProvider
と Region:
には環境変数 AWS_REGION
から読み取った情報を利用する。
// 以下の AWS_REGION は String となるので注意 svc := s3.New(&aws.Config{Credentials: credentialsProvider, Region: aws.String(os.Getenv("AWS_REGION"))})
以上。