I have a project built in Golang and deployed on a Docker instance in AWS.
Internally I create a log file where the program write several logs.
How can I access that log file?
Is there another correct way to logging?
Thanks
You could mount the log file from your container to your EC2 host. You can do this by using the -v flag when running your container:
docker run -v /var/log/my_host_log_file.log:/var/log/your_container_log_file.log your-image
Alternatively, you can configure your app to log to stdout and use syslog as your log driver (using the --log-driver=syslog switch). Your container logs will then be written to /var/log/messages on your host.
If you use AWS, i would suggest to send Logs direct to AWS CloudWatch.
First create a new Log-Group in AWS Cloudwatch, for example "Production". In your Docker-Compose.yml (or via docker run..) add the AWS Logdriver:
logging:
driver: "awslogs"
options:
awslogs-region: "eu-central-1"
awslogs-group: "Production"
awslogs-stream: "MyApp"
Next creat a IAM user with Access to AWS Cloudwatch and add to the Dockerhost the credentials.
Example IAM Policy:
"Version" "2012-10-17"
"Statement"
"Action" "logs:CreateLogStream" "logs:PutLogEvents" "Effect" "Allow" "Resource"
On Ubuntu with systemd:
"Version" "2012-10-17"
"Statement"
"Action"
"logs:CreateLogStream"
"logs:PutLogEvents"
"Effect"
"Allow" "Resource"
And add to the File:
[Service] Environment"AWS_ACCESS_KEY_ID=<aws_access_key_id>"
Environment"AWS_SECRET_ACCESS_KEY=<aws_secret_access_key>"
Run:
systemctl daemon-reload
service docker restart
Now your logs should appear in AWS Cloudwatch.
Thanks for reply.
After a while looking for the solution to the problem, I found it!
Firstly, I needed to mount the file that is inside the instance in the docker-host.
To do this I add a Json file in the root folder of my project called Dockerrun.aws.json
( http://docs.aws.amazon.com/es_es/elasticbeanstalk/latest/dg/create_deploy_docker_image.html#create_deploy_docker_image_dockerrun )
That is the file that declares the shared folder (volumes) (beetwen docker-host and instance) where I save my log file . This line is equivalent to adding -v flag in the docker run command (https://docs.docker.com/engine/tutorials/dockervolumes/#mount-a-host-directory-as-data-volume). I do this this way because I can not add mount to a running instance and i cant stop it by ssh.
{
"AWSEBDockerrunVersion": "1",
"Volumes": [
{
"HostDirectory": "/var/log/",
"ContainerDirectory": "/go/src/app/log"
}
]
}
Then to tell aws that I want to download my log file when I request records. (Tail (last 100 lines), bundle or rotate) I add these files to the .ebextension folder in my project directory. ( http://docs.aws.amazon.com/en_us/elasticbeanstalk/latest/dg/using-features.logging.html#health-logs-extend )
Log_bundle.conf
Files:
"/opt/elasticbeanstalk/tasks/bundlelogs.d/log_bundle.conf":
Mode: "000755"
Owner: root
Group: root
Content: |
/var/log/application.log
Log_rotate.config
Files:
"/opt/elasticbeanstalk/tasks/bundlelogs.d/log_rotate.conf":
Mode: "000755"
Owner: root
Group: root
Content: |
/var/log/application.log
Log_tail.config
Files:
"/opt/elasticbeanstalk/tasks/publishlogs.d/log_tail.conf":
Mode: "000755"
Owner: root
Group: root
Content: |
/var/log/application.log
Finally, I dont try Amazon Could Watch but is the next step.
Regards
If you use ELK (Elasticsearch, Logstash, Kibana), I would suggest to use "logrus"
Get the library
go get github.com/sirupsen/logrus
Then in your project
package main
import (
logrus "github.com/sirupsen/logrus"
)
var log = logrus.New()
func main() {
conn, _ := net.Dial("tcp", "logstash-address")
hook := logrustash.New(conn, logrustash.DefaultFormatter(logrus.Fields{"type": "my-app"}))
log.Hooks.Add(hook)
log.Info("Hello World!")
}
Related
I have an AWS Elasticbeanstalk Python environment and I'd like to change default 1 MB nginx configuration limit for file upload to a bigger value (15 MB).
So I thought to add a config file in .ebextensions:
files:
"/tmp/my.nginx.conf" :
mode: "000755"
owner: root
group: root
content: |
client_max_body_size 15M;
"/tmp/install-nginx-config.sh" :
mode: "000755"
owner: root
group: root
content: |
#!/bin/sh
cp /tmp/my.nginx.conf /etc/nginx/conf.d/elasticbeanstalk/01_max-size.conf
container_commands:
01_runmyshellscript:
command: "sudo /tmp/install-nginx-config.sh"
02_reload_nginx:
command: "sudo service nginx reload"
The idea is to add the new config file in /etc/nginx/conf.d/elasticbeanstalk after the elasticbeanstalk nginx config files are created, so to no to to interfere with the creation of the EB environment, like suggested in many articles on Internet.
But if I deploy the above file in .ebextensions then the environment fails and if I SSH the EC2 I find out that in /etc/nginx/conf.d/elasticbeanstalk there is my 01_max-size.conf but the nginx configuration file created by Elasticbeanstalk isn't there anymore.
This is strange, beacause container_commands should be exetuced after the end of environment creation, so I have no clue of how solve this issue.
I basically followed this web page. https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-on-EC2-Instance-fleet.html
Steps
Launch an EC2 instance (Amazon Linux 2) with an IAM role (Permissions: CloudWatchAgentServerRole, AmazonSSMManagedInstanceCore).
See "Download the CloudWatch agent package" section in the documentation and run "AWS-ConfigureAWSPackage".
Go to Systems Manager Parameter Store and create a parameter.
Name: AmazonCloudWatch-linux
Parameter: see below
{
"metrics": {
"append_dimensions": {
"ImageId": "${!aws:ImageId}",
"InstanceId": "${!aws:InstanceId}",
"InstanceType": "${!aws:InstanceType}"
},
"metrics_collected": {
"mem": {
"measurement": [
"mem_used_percent"
],
"metrics_collection_interval": 60
}
}
}
}
See "Start the CloudWatch agent" section in the documentation and run "AmazonCloudWatch-ManageAgent". I input "AmazonCloudWatch-linux" to the "Optional Configuration Location" box.
To check the status of CloudWatch Agent, I run sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -m ec2 -a status, and this below is returned. This means CloudWatch Agent is running successfully.
{
"status": "running",
"starttime": "2022-07-20T15:06:12+0000",
"configstatus": "configured",
"cwoc_status": "running",
"cwoc_starttime": "2022-07-20T15:06:11+0000",
"cwoc_configstatus": "configured",
"version": "1.247353.0b251941"
}
I also go to CloudWatch Metrics and confirm I get a new metric.
However, the execution history of "AmazonCloudWatch-ManageAgent" (Step 4) has some messages in "Error".
Created symlink from /etc/systemd/system/multi-user.target.wants/cwagent-otel-collector.service to /etc/systemd/system/cwagent-otel-collector.service.
Redirecting to /bin/systemctl restart cwagent-otel-collector.service
2022/07/20 15:06:12 D! [EC2] Found active network interface
2022/07/20 15:06:12 Reading json config file path: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/ssm_AmazonCloudWatch-linux.tmp ...
2022/07/20 15:06:12 I! Valid Json input schema.
2022/07/20 15:06:12 D! [EC2] Found active network interface
Created symlink from /etc/systemd/system/multi-user.target.wants/amazon-cloudwatch-agent.service to /etc/systemd/system/amazon-cloudwatch-agent.service.
Redirecting to /bin/systemctl restart amazon-cloudwatch-agent.service
I also check "Output" of the execution history. In my understanding, this does not show any issues.
****** processing cwagent-otel-collector ******
Successfully fetched the config and saved in /opt/aws/amazon-cloudwatch-agent/cwagent-otel-collector/etc/cwagent-otel-collector.d/default.tmp
cwagent-otel-collector config has been successfully fetched.
cwagent-otel-collector has already been stopped
****** 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 ssm:AmazonCloudWatch-linux --mode ec2 --config /opt/aws/amazon-cloudwatch-agent/etc/common-config.toml --multi-config default
I! Trying to detect region from ec2
Region: ap-northeast-1
credsConfig: map[]
Successfully fetched the config and saved in /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/ssm_AmazonCloudWatch-linux.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
I! Detecting run_as_user...
I! Trying to detect region from ec2
No csm configuration found.
No log 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
Question
Even though there is no problem at Step 5, 6 and 8, why is there a message in Error at Step 7?
I'm migrated from AL1 to AL2 on AWS Beanstalk. AL2 changed location of my nodejs.log to /var/log/{{.}}.stdout.log
I resolved this by adding ryslog.config to .ebexetensions:
files:
"/opt/elasticbeanstalk/config/private/rsyslog.conf.template":
mode: "000644"
owner: root
group: root
content: |
# This rsyslog file redirects Elastic Beanstalk platform logs.
# Logs are initially sent to syslog, but we also want to divide
# stdout and stderr into separate log files.
template(name="SimpleFormat" type="string" string="%msg%\n")
$EscapeControlCharactersOnReceive off
{{range .ProcessNames}}if $programname == '{{.}}' then {
*.=warning;*.=err;*.=crit;*.=alert;*.=emerg /var/log/nodejs/nodejs.log; SimpleFormat
*.=info;*.=notice /var/log/nodejs/nodejs.log; SimpleFormat
}
{{end}}
Above configuration is working but I have problem with log file permissions.
Directory /var/log/nodejs and nodejs.log file are only readable by root (chmod 600), and cloudwatch-agent can't read it. Changing permissions manually do the job, but how can I change the permissions to be created automatically on beanstalk deploy?
Adding the following code resolved it.
This will set the owner and group to the corresponding value, for all files that are automatically created.
# Set the default permissions for all log files
$umask 0022
$FileOwner cwagent
$FileGroup cwagent
$DirOwner cwagent
$DirGroup cwagent
I am trying to send two lots of logs up to CloudWatch.
Here are the two logs:
/var/log/apache2/access.log
/var/log/apache2/error.log
I used the amazon-cloudwatch-agent-config-wizard to create the config file, and here is a snippet of the file showing the correct file path:
"collect_list": [
{
"file_path": "/var/log/apache2/access.log",
"log_group_name": "*group_name*",
"log_stream_name": "apache-access"
},
{
"file_path": "/var/log/apache2/error.log",
"log_group_name": "group-name*",
"log_stream_name": "apache-error"
}
]
I loaded in the config with:
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json -s
And ran into no errors and no errors and showing in the amazon-cloudwatch-agent.log.
Checking the status of the Amazon CloudWatch Agent shows it is running and has not errors. Also states the schema is valid.
The unique part of all of this is that I have removed the old CloudWatch agent and installed the new one. I have done this on two EC2 instances, one of which everything is working perfectly on, and the other one is not sending the logs to CloudWatch.
In a nutshell, why aren't the logs going up to CloudWatch? What can I do to troubleshoot this?
Any help will be appreciated.
So the problem turned out to be permission-based. The CloudWatch config wizard defaults to using cwagent as the user that runs CloudWatch, this is also reiterated in official guides.
Changing the running using to root resolved the issue even though the files in question all had 777 permissions at the time of trying to get it running.
The config file you edit is:
sudo nano /opt/aws/amazon-cloudwatch-agent/bin/config.json
At the top of the file you will see:
"agent": {
"metrics_collection_interval": 60,
"run_as_user": "cwagent"
},
You need to change run_as_user to root, like:
"agent": {
"metrics_collection_interval": 60,
"run_as_user": "root"
},
Once you have changed that, you simply reload the config file:
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json -s
And then restart the service:
sudo systemctl restart amazon-cloudwatch-agent.service
You should then see the logs coming into CloudWatch. Expect some backfilling.
Check
the CloudWatch Agent log- /opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log.
Here you should see some lines similar to - Reading from /var/log/apache2/access.log for both the files.
the agent toml file to make sure both the files path are configured and check the region as well-
/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.toml.
We are trying to store environment specific application configuration files in s3.
The files are stored in different subdirectories which are named after the environment and also have the environment as part of the file name.
Examples are
dev/application-dev.properties
stg/application-stg.properties
prd/application-prd.properties
The Elastic Beanstalk environments are named dev, stg, prd and alternatively I also have an environment variable defined in Elastic Beanstalk named ENVIRONMENT which can be dev, stg or prd.
My question now is, how do I reference the environment name or ENVIRONMENT variable when downloading the configuration file from a config file in .ebextensions?
I tried using a {"Ref": "AWSEBEnvironmentName" } reference in .ebextensions/myapp.config but get a syntax error when deploying.
The content of .ebextensions/myapp.config is:
files:
/config/application-`{"Ref": "AWSEBEnvironmentName" }`.properties:
mode: "000666"
owner: webapp
group: webapp
source: https://s3.amazonaws.com/com.mycompany.mybucket/`{"Ref": "AWSEBEnvironmentName" }`/application-`{"Ref": "AWSEBEnvironmentName" }`.properties
authentication: S3Access
Resources:
AWSEBAutoScalingGroup:
Metadata:
AWS::CloudFormation::Authentication:
S3Access:
type: S3
roleName: aws-elasticbeanstalk-ec2-role
buckets: com.mycompany.api.config
The error I get is:
The configuration file .ebextensions/myapp.config in application version
manualtest-18 contains invalid YAML or JSON. YAML exception: Invalid Yaml:
mapping values are not allowed here in "<reader>", line 6, column 85:
... .config/stg/application-`{"Ref": "AWSEBEnvironmentName" }`.prop ... ^ ,
JSON exception: Invalid JSON: Unexpected character (f) at position 0..
Update the configuration file.
What is the correct way of referencing an environment variable in a .ebextensions config file in AWS Elastic Beanstalk?
Your .ebextensions config file was almost correct. Substituting the file name with environment variable or AWS resource name won't work, for that do as in Mark's answer to rename the file created in container_commands section.
The source option value trying to access AWS resource name using Ref was correct, it just had to be surrounded by single quote ', like below:
files:
/config/application.properties:
mode: "000666"
owner: webapp
group: webapp
source: 'https://s3.amazonaws.com/com.mycompany.mybucket/`{"Ref": "AWSEBEnvironmentName" }`/application-`{"Ref": "AWSEBEnvironmentName" }`.properties'
authentication: S3Access
And to access environment variables use Fn::GetOptionSetting. Environment variables are in aws:elasticbeanstalk:application:environment namespace.
Below example access an environment variable ENVIRONMENT in source option of files:
files:
"/tmp/application.properties" :
mode: "000666"
owner: webapp
group: webapp
source: 'https://s3.amazonaws.com/com.mycompany.mybucket/`{"Ref": "AWSEBEnvironmentName" }`/application-`{"Fn::GetOptionSetting": {"Namespace": "aws:elasticbeanstalk:application:environment", "OptionName": "ENVIRONMENT ", "DefaultValue": "dev"}}`.properties'
authentication: S3Auth
I struggled to get this working, until I discovered that the Sub function doesn't appear to be available in ebextensions: http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/ebextensions-functions.html
This means that you need to fall back to Fn::Join and Ref, at least until support for Sub is introduced to ebextensions. It also seems that the files attribute requires a fixed path (and I couldn't use Fn::Join in this context).
My overall solution to this was as follows:
Resources:
AWSEBAutoScalingGroup:
Metadata:
AWS::CloudFormation::Authentication:
S3Auth:
type: S3
buckets: arn:aws:s3:::elasticbeanstalk-xxx
roleName: aws-elasticbeanstalk-ec2-role
files:
"/tmp/application.properties" :
mode: "000644"
owner: root
group: root
source: { "Fn::Join" : ["", ["https://s3-xxx.amazonaws.com/elasticbeanstalk-xxx/path/to/application-", { "Ref" : "AWSEBEnvironmentName" }, ".properties" ]]}
authentication: S3Auth
container_commands:
01-apply-configuration:
command: mkdir -p config && mv /tmp/application.properties config
This will result in an application.properties file (without the environment name qualifier) in a config directory next to the deployed application instance.
If you want to keep the name of the environment as part of the file name using this approach, you will need to adjust the command that moves the file to use another Fn::Join expression to control the filename.
You are almost there .ebextensions are using YAML format, while your trying to use JSON. Use Ref: AWSEBEnvironmentName.
In addition, you can take advantage of Sub function to avoid pesky Join:
!Sub "/config/application-${AWSEBEnvironmentName}.properties"