ようへいの日々精進XP

よかろうもん

Docker をソースコードからビルドする + α

追記

Logging Driver の件、追記した。

inokara.hateblo.jp

samuelkarp さん、本当に有難うございました。

tl;dr

思う所があり以下を試してみた。

  • Docker をソースコードからビルドして開発中のバージョンを利用する
  • Logging Driver に CloudWatch Logs が追加されそうなので試してみる
  • AWS SDK for Go を少し触ってみる

Docker をソースコードからビルドして開発版を利用する手順

概要

  • ビルド手順はコンテナを利用して行われる
  • 最新の Docker が必要
  • ビルド→バイナリ生成が完了すると bundles 以下にバイナリが生成されている

参考

ビルド済みのイメージを利用しても良いかもしれない。

動作確認環境

$ 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 をインストール

$ 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 の動作確認

トィウィッターを見ていたら...

おお、スゴイ!と思ったので試してみることにした。

注意

あくまでも開発中の機能の為、実際のリリースの際に提供される機能とは異なる場合があるのでご注意を...。お試しの際は自己責任で宜しくお願いいたします。

参考

上記 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 のコンソールでもログを確認することが出来た。

f:id:inokara:20150925151347p:plain

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

ログを確認してみると以下のように記録されている。

f:id:inokara:20150925153418p:plain


AWS SDK for Go の Credential 設定について

モチベーション

上記の通りコンテナから CloudWatch Logs にログが飛ばない(Log Stream が作成されない)件を調査の流れから...。

参考

よく解ってないけど書いてみた

ドキュメントの読み方すらよく理解していないけど、以下のように書いてみた。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/credentialsdefault に定義されている情報を利用出来るようにした credentialsProvider を定義している。

そして S3 への接続(クライアントの作成)時に以下のように Credentials:credentialsProviderRegion: には環境変数 AWS_REGION から読み取った情報を利用する。

// 以下の AWS_REGION は String となるので注意
svc := s3.New(&aws.Config{Credentials: credentialsProvider, Region: aws.String(os.Getenv("AWS_REGION"))})

以上。