iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0

Laravel 有 log 記錄各種錯誤、發生的事情以及開發、debug 需要的訊息,這些 log 在 Laravel 執行在 container 內後都存在 container 內。如果執行在 AWS 的 Laravel 需要 log 資訊來 debug,在開著多台 container 的情況下要連進 container 找到出錯的 log 相當費時費力,而且 container 可能因為 deploy 等因素被關閉,寶貴的 log 可能就這麼隨風而逝……所以我們希望把 log 放在一個比較好查詢及保存的地方,在 AWS 裡就是使用 CloudWatch 這個服務。(本日程式碼

CloudWatch 是 AWS 提供監控功能的服務,可以收集 log、監控各種服務與系統狀況以及在某些條件下做一些反應。今天要使用收集 log 的部分,我們會收集 container 執行的 log 以及 Laravel application 層的 log。

CloudWatch 收集、儲存 log 的地方稱為 log group,每個 log group 裡會有一到多個 log stream,每個 log stream 分別來自不同來源,像是不同 EC2 instance。

Laravel 相關 log

先來讓 Laravel 的 log 可以送到 cloudwatch,方便在出現 error 時能從 cloudwatch 找相關 log 來 debug。如果沒有把 log 送到 cloudwatch,出現問題時只能連進 EC2 instance 再進入一個個 container 檢查 log。container 只有一兩個的時候還好,再多幾個就……青春都耗費在找 log 上了 😰

安裝 cloudwatch agent

要把 EC2 instance 或 container 的 log 檔案傳到 cloudwatch,要在 EC2 instance 或 container 上安裝 cloudwatch agent。現在我們要收集的是執行 Laravel 的 container 內的 log,在 Dockerfile 加入以下指令安裝及設定 cloudwatch agent:

# install amazon-cloudwatch-agent
RUN curl -O https://s3.amazonaws.com/amazoncloudwatch-agent/debian/amd64/latest/amazon-cloudwatch-agent.deb && \
    dpkg -i -E amazon-cloudwatch-agent.deb && \
    rm -f amazon-cloudwatch-agent.deb

# cloudwatch config
COPY docker/cloudwatch/amazon-cloudwatch-agent.json /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
COPY docker/cloudwatch/amazon-cloud-watch.conf /etc/supervisor/conf.d/amazon-cloud-watch.conf

這段指令是依照文件下載及安裝 deb,再把 cloudwatch agent 的設定檔放到對應位置 (ref)。另外我們要用 supervisord 啟動並維持 cloudwatch agent 的運作,所以要放一個 cloudwatch 的設定檔給 supervisord。

設定 cloudwatch agent

在 cloudwatch agent 設定檔 amazon-cloudwatch-agent.json 指定哪些檔案的內容要傳給 cloudwatch、傳到哪個 log group,我們可以把 Laravel 的 log 跟 container 的 crontab log 傳去 cloudwatch:

{
    "agent": {
        "run_as_user": "root"
    },
    "logs": {
        "logs_collected": {
            "files": {
                "collect_list": [
                    {
                        "file_path": "/var/www/html/storage/logs/laravel.log",
                        "log_group_name": "/my-app/laravel.log"
                    },
                    {
                        "file_path": "/var/log/cron.log",
                        "log_group_name": "/my-app/cron.log"
                    }
                ]
            }
        },
        "log_stream_name": "{instance_id}/{local_hostname}"
    }
}

log_stream_name 是指定 container 送出的 log 所在的 log stream。{instance_id}{local_hostname} 是變數,{instance_id} 是 EC2 instance 的 instance ID,{local_hostname} 在 container 內會是 container id。log_stream_name 設為 EC2 instance ID 跟 container id 的字串,這樣比較容易知道 log 是哪個 task 的。

supervisor 啟動 cloudwatch 的設定

跟 Laravel worker 一樣,用 supervisord 啟動與維持 cloudwatch agent 的運作。設定檔 amazon-cloud-watch.conf

[program:amazon-cloud-watch]
command=/opt/aws/amazon-cloudwatch-agent/bin/start-amazon-cloudwatch-agent
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stopwaitsecs=3600

Terraform log group resource

用 terraform 管理 resource 後,針對 cloud resource 的修改都改用 terraform 了~ terraform 定義 log group 的 resource,能夠設定 log group 名稱跟 log 的保留天數(retention_in_days):

resource "aws_cloudwatch_log_group" "app" {
  name              = "/my-app/laravel.log"
  retention_in_days = 30
}

resource "aws_cloudwatch_log_group" "app_cron" {
  name              = "/my-app/cron.log"
  retention_in_days = 14
}

cloudwatch log group 頁面

順利的話就能在 cloudwatch 的 log group 裡找到 Laravel container 的 log group:

https://ithelp.ithome.com.tw/upload/images/20231002/20160671jLmGkp8P9h.png

每個 log stream 來自一個 container,可以進到 log stream 看那個 container 的 log,也能夠從 log group 頁面點 Search all log streams 一次查看所有 log stream。

https://ithelp.ithome.com.tw/upload/images/20231002/20160671f9pDDseuz4.png

cloudwatch agent 的 log

如果沒那麼順利……過了幾分鐘 cloudwatch log group 內還是沒有 log stream 的話,就要進 container 看看 cloudwatch agent 是不是有什麼問題。先連進 EC2 instance,接著用 docker exec -it [CONTAINER_ID] bash 指令進到 container ,再查看 cloudwatch agent 本身的 log /var/log/amazon/amazon-cloudwatch-agent/amazon-cloudwatch-agent.log

ECS task 的 log

ECS task 的 log 功能讓我們可以看到執行 container 時的 output,這個 output 是 container 的 entry script 的 output,log driver awslogs 只是單純把 Docker 得到的 log 傳給 cloudwatch。啟動 ECS task 的 log 功能要在 ecs task definition resourcecontainer definition 加上 log configuration

    logConfiguration = {
      logDriver = "awslogs"
      options = {
        awslogs-group         = "my-app-ecs-awslogs"
        awslogs-region        = var.region
        awslogs-create-group  = "true"
        awslogs-stream-prefix = "my-app"
      }
    }

這樣設定讓 ECS task 在變數 region 指定的 region 建立 my-app-ecs-awslogs log group,裡面會有像 my-app/my-app/ea1e930c2ad5452d9cbf924699891fff 這樣的 log stream,log stream 名稱格式prefix-name/container-name/ecs-task-id

接著加入能建立 log group、log stream 以及寫 log 的 IAM role 跟 policy:

resource "aws_iam_role" "ecs_task_exec" {
  name = "my-app-ecs-task-exec-role"
  assume_role_policy = jsonencode({
    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Effect" : "Allow",
        "Principal" : {
          "Service" : "ecs-tasks.amazonaws.com"
        },
        "Action" : "sts:AssumeRole"
      }
    ]
  })
}

resource "aws_iam_role_policy" "ecs_task_exec" {
  role = aws_iam_role.ecs_task_exec.id
  policy = jsonencode({
    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Effect" : "Allow",
        "Action" : [
          "ecr:GetAuthorizationToken",
          "ecr:BatchCheckLayerAvailability",
          "ecr:GetDownloadUrlForLayer",
          "ecr:BatchGetImage",
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ],
        "Resource" : "*"
      }
    ]
  })
}

如果前面 awslogs-create-grouptrue ,ecs task 或 EC2 就要有 logs:CreateLogGroup 的權限。

最後把 IAM role 設定給 aws_ecs_task_definition.tdexecution_role_arn

execution_role_arn       = aws_iam_role.ecs_task_exec.arn

ECS task 的 execution role 是給 ECS agent 的 role,讓 ECS agent 可以 call AWS 的 API。

ECS task 有另一個 role 是 task role,這個 role 是給 container 使用的。如果 Laravel 需要權限使用 AWS 的服務,可以把需要的權限放在 IAM role 並且設定成 task role。這樣 Laravel 只要是以 ECS service 跑起來,就擁有需要的 AWS 權限,以這個方式給 Laravel 權限可以避免 AWS 的 credential 隨著 docker image 外流。

從 ECS 看 task log

ECS task log 不像前面自己送到 cloudwatch 的 log 得到 cloudwatch 找 log group,從 ECS service 跟 task 頁面的 Logs tab 就能看到。

ECS service 的 log 會顯示所有 task 的 log:

https://ithelp.ithome.com.tw/upload/images/20231002/20160671yrHiZtCB0X.png

某個 task 當然就是顯示自己的 log:

https://ithelp.ithome.com.tw/upload/images/20231002/20160671x1wm7qRl3v.png

Ref


上一篇
Day 21 以 Gitlab 作為 Terraform backend
下一篇
Day 23 把 EC2 instance 移到 private subnet
系列文
AWS ECS + Gitlab + Laravel + Terraform 從入門到摔坑30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言