티스토리 뷰
AWS Elastic Beanstalk Amazon Linux2 환경에서의 로그 커스터마이징 (Amazon Cloudwatch & EB Web Console)
Neil 2021. 7. 12. 22:45
지난 블로깅을 통해 Amazon Linux 1 환경의 Elastic Beanstalk 어플리케이션을 새로운 Amazon Linux2 환경으로 마이그레이션 하는 과정을 소개하였다. 이번 포스팅에서는 Elastic Beanstalk 웹 콘솔과 Amazon Cloudwatch 를 통해서 내가 원하는 로그를 볼 수 있는 기능을 소개하려고 한다.
우리의 API 서버인 sachiel 은 모니터링을 위해 API 호출과 exception 로그를 분리해서 저장하고 있었다. 각각 /var/log/sachiel/info\_api\_call.log
와/var/log/sachiel/exception.log
경로로 저장하고 있었는데, 아무런 설정을 하지 않는다면 Amazon Cloudwatch 와 Elastic Beanstalk 웹 콘솔을 통해 확인할 수 없다. 이 서비스들로 로그를 열람하기 위해 했던 세팅을 소개한다.
🏗 삽질을 위한 준비물 — EC2 Session Manager
물론 VPN과 pem 키를 통해 인스턴스에 접근할 수 있겠지만, 실험하기 은근 귀찮다. elastic beanstalk 의 인스턴스를 웹콘솔의 Session Manager를 통해 접속한다면 더 효율적인 실험이 가능하다. aws elastic beanstalk 로 만드는 ec2 의 iam role 에 아래의 policy를 추가하도록 하자.
aws iam attach-role-policy --role-name <EC2-ROLE-NAME> --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
CLI 기준으로 적어뒀지만, 어떤 방식으로 하던 상관없다. 적용되었다면 아래와 같은 화면을 확인할 수 있다.
💭 삽질 #1 — Amazon Linux 2 환경에서는 기본으로 awslogs 가 없네 - amazon-cloudwatch-agent
Amazon Linux 1 환경에서는 awslogs 라는 데몬이 default 로 사용되었지만, Amazon Linux 2 에서는 아니었다. 배포 과정 로그를 살펴보니 amazon-cloudwatch-agent 라는 어플리케이션이 로그를 스트리밍하고 있음을 파악할 수 있었다. 덕분에(??) 기존에 사용하던 .ebextensions 안의 /etc/awslogs/config/ 관련 files 옵션이 무의미해졌다.
2021/05/21 06:40:42.583724 [INFO] Executing instruction: configure log streaming
2021/05/21 06:40:42.583734 [INFO] start to get cloudwatch log client
2021/05/21 06:40:42.583834 [INFO] start to config log streaming
2021/05/21 06:40:42.583840 [INFO] start to configure log streaming config file
2021/05/21 06:40:42.584026 [INFO] start to create cloudwatch log stream
2021/05/21 06:40:42.584030 [INFO] start to create log streaming
2021/05/21 06:40:42.742418 [INFO] start to fetch config
2021/05/21 06:40:42.742449 [INFO] Running command /bin/sh -c /opt/aws/amazon-cloudwatch-agent/bin/amazon--ctl -a fetch-config -c file:/opt/aws/amazon-cloudwatch-agent/etc/beanstalk.json -s
2021/05/21 06:40:43.421586 [INFO] ****** processing amazon-cloudwatch-agent ******
/opt/aws/amazon-cloudwatch-agent/bin/config-downloader --output-dir /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d --download-source file:/opt/aws/amazon-cloudwatch-agent/etc/beanstalk.json --mode ec2 --config /opt/aws/amazon-cloudwatch-agent/etc/common-config.toml --multi-config default
Successfully fetched the config and saved in /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/file_beanstalk.json.tmp
Start configuration validation...
/opt/aws/amazon-cloudwatch-agent/bin/config-translator --input /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json --input-dir /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d --output /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.toml --mode ec2 --config /opt/aws/amazon-cloudwatch-agent/etc/common-config.toml --multi-config default
Valid Json input schema.
I! Detecting run_as_user...
No csm configuration found.
No metric configuration found.
Configuration validation first phase succeeded
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent -schematest -config /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.toml
Configuration validation second phase succeeded
Configuration validation succeeded
amazon-cloudwatch-agent has already been stopped
/opt/aws/amazon-cloudwatch-agent/etc/file_beanstalk.json 을 직접 수정해보기로 했다. 이 작업을 위해 나는 새로운 기능인 .platform/hooks/postdeploy 훅을 시도해봤다. 전 포스팅에서 짧게 소개했지만, 배포 이후의 시점에서 커스터마이징할 수 있는 hook이다. 아래의 구조로 구성했다. (chmod 는 필수다.)
.platform/
└── hooks
└── postdeploy
├── 01_edit_amazon_cloudwatch_agent_conf.py (chmod +x)
└── 02_restart_amazon_cloudwatch_agent.sh (chmod +x)
01_edit_amazon_cloudwatch_agent_conf.py 는 아래와 같이 작성했다. 데몬의 config파일을 직접 수정하는 스크립트다.
#!/usr/bin/env python3
import json
file_path = '/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/file_beanstalk.json'
with open(file_path, 'r') as f:
jj = json.load(f)
collect_list = jj['logs']['logs_collected']['files']['collect_list']
one_entity = collect_list[0]
one_entity_log_group_name = one_entity['log_group_name']
path = '/'.join(one_entity_log_group_name.split('/')[:-1])
new_collect_list = [
{
'file_path': '/var/log/sachiel/info.log',
'log_group_name': path + '/sachiel/info.log',
'log_stream_name': '{instance_id}'
},
{
'file_path': '/var/log/sachiel/info_api_call.log',
'log_group_name': path + '/sachiel/info_api_call.log',
'log_stream_name': '{instance_id}'
},
{
'file_path': '/var/log/sachiel/info_payment.log',
'log_group_name': path + '/sachiel/info_payment.log',
'log_stream_name': '{instance_id}'
},
{
'file_path': '/var/log/sachiel/exception.log',
'log_group_name': path + '/sachiel/exception.log',
'log_stream_name': '{instance_id}'
},
]
collect_list += new_collect_list
new_cloudwatch_logs = {
'logs': {
'logs_collected': {
'files': {
'collect_list': collect_list
}
}
}
}
with open(file_path, 'w') as f:
json.dump(new_cloudwatch_logs, f)
02_restart_amazon_cloudwatch_agent.sh 는 아래와 같이 작성했다. 데몬의 config 파일을 수정했으니, 데몬을 리스타트하는 것 뿐이다.
#!/bin/bash
service amazon-cloudwatch-agent restart
그래서 원하는 목적을 달성했다. Cloudwatch 의 로그 그룹에서 드디어 내가 원하는 로그를 확인할 수 있었다.
하지만 지금은 사용하지 않는다. 배포 시간도 좀 더 잡아먹었고, 내가 작성한 python 스크립트가 Elastic Beanstalk 판올림 같은 피치 못할 이유로 언제 터질지 모르는 불안정한방법이라고 생각했기 때문이다. (그럼에도 불구하고 AWS 포럼에서 이런 비슷한 식으로 사용하는 사람들이 있었다.)
💭 삽질 #2 — awslogs 를 쓸 수 있다고?
yum 을 통해 기존에 쓰던 awslogs 를 설치하고 사용할 수 있음을 인스턴스 안에서 확인했다. Amazon Linux 1 인스턴스에서 얻은 정보와 로그를 통해 많은 정보를 알아냈고, 새로운 Amazon Linux 2 인스턴스에서 아래의 과정을 거쳤다.
yum install awslogs -y
systemctl enable awslogsd.service
systemctl restart awslogsd
원하는 대로 작동하는 것을 확인하고나서 .ebextensions 를 수정하였다. 아래와 같은 항목들을 추가했다.
packages:
yum:
awslogs: []
commands:
02-awslog:
command: systemctl enable awslogsd.service
03-awslog:
command: systemctl restart awslogsd
files:
"/etc/awslogs/awslogs.conf" :
mode: "000600"
owner: root
group: root
content: |
[general]
state_file = /var/lib/awslogs/agent-state
"/etc/awslogs/config/sachiel.conf":
mode: "000600"
owner: root
group: root
content: |
[info.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", "sachiel", "var/log/sachiel/info.log"]]}`
log_stream_name = `{"Fn::Join":["/", [{ "Ref":"AWSEBEnvironmentName" }, "{instance_id}"]]}`
file = /var/log/sachiel/info.log
[info_api_call.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", "sachiel", "var/log/sachiel/info_api_call.log"]]}`
log_stream_name = `{"Fn::Join":["/", [{ "Ref":"AWSEBEnvironmentName" }, "{instance_id}"]]}`
file = /var/log/sachiel/info_api_call.log
[exception.log]
log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", "sachiel", "var/log/sachiel/exception.log"]]}`
log_stream_name = `{"Fn::Join":["/", [{ "Ref":"AWSEBEnvironmentName" }, "{instance_id}"]]}`
file = /var/log/sachiel/exception.log
/etc/awslogs/config 안에 .conf 파일을 추가하면 그 추가 config 파일도 같이 추가되어 로그를 스트리밍하는 구조로 파악했다. 근거는 아래와 같다.
[root@dv-sachiel-ap-northeast-2b-046f bin]# systemctl status awslogsd
● awslogsd.service - awslogs daemon
Loaded: loaded (/usr/lib/systemd/system/awslogsd.service; enabled; vendor preset: disabled)
Active: active (running) since Fri 2021-05-21 06:39:56 UTC; 56min ago
Main PID: 2749 (aws)
CGroup: /system.slice/awslogsd.service
└─2749 /usr/bin/python2 -s /usr/bin/aws logs push --config-file /etc/awslogs/awslogs.conf --additional-configs-dir /etc/awslogs/config
May 21 06:39:56 ip-10-210-35-80.ap-northeast-2.compute.internal systemd[1]: Started awslogs daemon.
May 21 06:39:56 ip-10-210-35-80.ap-northeast-2.compute.internal systemd[1]: Starting awslogs daemon...
그리고 프로비저닝을 새롭게 시도하였다. 원하는대로 프로비저닝에 성공하였음을 알 수 있었다. 웹 콘솔에서 로그 그룹을 진입하면 현재 로그 스트림을 확인할 수 있었다.
💭 삽질 #3 — /opt/elasticbeanstalk 에 파일을 작성하자.
Elastic Beanstalk 웹 콘솔에서도 로그를 보는 기능이 있다. 전체 로그를 다운받는 기능과, 마지막 tail 로그를 가져오는 기능이 있는데 이 기능들을 통해서 커스텀 로그 파일들을 가져오려면 .ebextensions 안에 아래와 같이 등록하면 된다.
"/opt/elasticbeanstalk/tasks/bundlelogs.d/sachiel.conf" :
mode: "000755"
owner: root
group: root
content: |
/var/log/sachiel/*.log
"/opt/elasticbeanstalk/tasks/taillogs.d/sachiel.conf" :
mode: "000755"
owner: root
group: root
content: |
/var/log/sachiel/*.log
https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.logging.html#health-logs-extend 를 참고하였으며, 파일이 잘 들어오는 것을 확인할 수 있다.
var 15 라는건 15번 다운받아봤다는… 이야기 🤯
요약
Elastic Beanstalk 가 Amazon Linux 2 환경을 발표하면서 일관성을 갖추려는 시도가 있었던 것 같다. 저번 포스팅을 통해서도 이야기했지만, Amazon Linux 2 환경으로 업그레이드하며 알려지지 않은 변경점들이 많다. 문서에 나와있지 않은 많은 부분들을 직접 알아내야 했다.
실제로 한 번 직접 마이그레이션을 겪어보니 다른 어플리케이션의 두 번째 마이그레이션은 생각보다 오래걸리지 않았다. 나의 두 포스트를 통해 많은 도움이 되길 바란다.
Reference
- https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.logging.html#health-logs-extend
- https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AgentReference.html
- https://github.com/awsdocs/elastic-beanstalk-samples/blob/master/configuration-files/aws-provided/instance-configuration/logs-streamtocloudwatch-linux.config
'Infrastructure' 카테고리의 다른 글
Github Actions 의 crontab 스케쥴링 기능으로 외주 프로젝트의 Certbot SSL 인증서 재발급 자동화하기 (0) | 2021.07.12 |
---|---|
AWS S3, Cloudfront, Route53과 github Actions으로 정적 웹페이지 배포, 호스팅 자동화하기 (0) | 2021.07.12 |
elastic beanstalk Amazon Linux2 python 환경 마이그레이션 후기 (0) | 2021.07.12 |
aws MFA OTP를 잃어버렸을 때 (Google Authenticator) - DevOps엔지니어가 한 스프린트 업무를 통째로 날릴뻔한 썰 (0) | 2021.04.06 |
Infrastructure as Code 를 읽고나서 (1) (0) | 2021.03.23 |