最近有收到需求是要將容器的日誌寫入 AWS CloudWatch 中,剛好 docker 的 logging drivers 有支援,紀錄一下過程

docker 預設的 logging driver 是 json-file,日誌檔案路徑在 /var/lib/docker/containers/<CONTAINER_ID>/<CONTAINER_ID>-json.log

這是 docker 的 logging drivers 支援的清單

logging driver 可以單一容器設定,也可以全域設定 (修改 /etc/docker/daemon.json)

下面關於 docker 的示範都是使用單一容器設定 logging driver

直接使用 awslogs logging driver

沒有傳遞 AWS Credentials 而直接使用 awslogs logging driver 的話會有下面錯誤

啟動容器

docker run --name test1 \
  --log-driver awslogs \
  --log-opt "awslogs-region=us-east-1" \
  --log-opt "awslogs-group=myLogGroup" \
  --log-opt "awslogs-stream=myLogStream" \
  busybox echo test1

查看一下狀態

docker container ls --all --filter name=test1
CONTAINER ID   IMAGE     COMMAND        CREATED         STATUS    PORTS     NAMES
e8fea7f1ce76   busybox   "echo test1"   7 seconds ago   Created             test1

發現無法啟動容器,容器的狀態是 Created,表示容器有成功從 Image 創建,但沒有成功啟動

找出錯誤原因

docker container inspect test1 | jq '.[0].State.Error'
failed to initialize logging driver: failed to create Cloudwatch log stream: NoCredentialProviders: no valid providers in chain. Deprecated.
For verbose messaging see aws.Config.CredentialsChainVerboseErrors

設定 AWS CloudWatch Policy

因為日誌要寫入 AWS CloudWatch 中,需要有相對應的 Credentials 及 Policy

Policy 最少需要 logs:CreateLogStream logs:PutLogEvents 這兩個 action,下面是我的 policy 設定

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogStream", // required
        "logs:PutLogEvents", // required
        "logs:CreateLogGroup" // 如果沒有給 CreateLogGroup 的話,需要事先建立 log group
      ],
      "Resource": [
        "arn:aws:logs:*:*:*"
      ]
    }
  ]
}

傳遞 AWS Credentials 參數給 docker daemon

有兩種方式傳遞 AWS Credentials

  1. 在 root 使用者家目錄下新增 AWS shared credentials file /root/.aws/credentials
  2. 新增 docker daemon 環境變數

下面過程是使用第二種方式

新增環境變數

sudo mkdir -p /etc/systemd/system/docker.service.d

cat << EOF | sudo tee /etc/systemd/system/docker.service.d/override.conf
[Service]
Environment="AWS_ACCESS_KEY_ID=<YOUR_AWS_ACCESS_KEY_ID>"
Environment="AWS_SECRET_ACCESS_KEY=<YOUR_AWS_SECRET_ACCESS_KEY>"
EOF

或是執行 sudo systemctl edit docker.service 指令,會進入編輯器進行編輯,完成編輯後依然是寫入相同檔案,兩者是一樣的

重新啟動 docker daemon

sudo systemctl daemon-reload
sudo systemctl restart docker.service

測試一下

docker run --name test1 \
  --log-driver awslogs \
  --log-opt "awslogs-region=us-east-1" \
  --log-opt "awslogs-group=myLogGroup" \
  --log-opt "awslogs-stream=myLogStream" \
  busybox echo test1

會在 CloudWatch 中看到 test1 的結果


參考資料: