この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。
CloudWatchの新機能として、ECSやEKSなどのコンテナワークロードのパフォーマンスとログデータを収集して分析することができる Container Insights が2019年9月にリリースされました。
Container Insightsの概要とECSで利用する設定手順については、リリース時に執筆されたブログ記事で紹介されています。
ECSやEKSのメトリクスを一括取得するContainer Insightsが一般公開!既存ECSクラスタも追加設定可能に! | Developers.IO
今回は、EKSでContainer Insightsを利用できるように設定してみました。
構成
設定手順
AWSドキュメント では、EKSでのContainer Insightsの設定方法について、2通りの方法が案内されています。
- 「クイックスタートセットアップ」で設定する
- ステップ・バイ・ステップで確認しながら設定する。
「1」の方法は、必要な設定を全て含んだKubernetesマニフェストを使うことで、一撃で設定を行うことができます。
「2」の方法は、一つずつ手順を追って設定する必要がありますが、EKSでContainer Insightsを利用できるようにするためにどのようなリソースや設定が必要なのか、問題が起きた時にどこを調べればよいのか、といったことを理解することができます。
今回は「2」の方法で進めることにします。
使用したツール類のバージョン
$ eksctl version
0.15.0
$ aws --version
aws-cli/1.18.26 Python/3.6.9 Linux/4.4.0-18362-Microsoft botocore/1.15.26
$ kubectl version --client --short
Client Version: v1.17.0
EKSクラスターの準備
Container Insightsを設定するEKSクラスターを準備します。
eksctl
コマンドを使用してクラスターを作成します。
eksctl create cluster \
--name eks-example \
--nodegroup-name ng-example \
--node-type t3.large \
--nodes 2 \
--managed
これで、ワーカーノードを2つ持つEKSクラスターが作成されます。
前提条件: ワーカーノードのIAMロールに必要なポリシーを追加する
Container Insightsの設定を始める前に、前提条件を確認します。
今回の環境で必要となるのは「必要なポリシーをワーカーノードのIAMロールに追加するには」の部分です。
マネジメントコンソールでEKSクラスターの情報を表示して、ノードグループ名をクリックします。
ノードグループの詳細画面で「ノードIAMロール名」をクリックします。
IAMロールにポリシーをアタッチします。
AWS管理ポリシー CloudWatchAgentServerPolicy
を選択して、アタッチします。
これで、ワーカーノード (コンテナが動作するEC2インスタンス) にポリシーが設定されました。
このポリシーを設定する目的は、この後の手順で設定する「CloudWatchエージェント」「Fluentd」のDaemonSetにより実行されるコンテナに対して「CloudWatchへメトリクスやログを送信する権限」を与えることです。
ステップ1: メトリクス収集を行うCloudWatchエージェントを導入する
以下のページの内容に沿って進めます。
クラスターメトリクスを収集するよう CloudWatch エージェントをセットアップする - Amazon CloudWatch
なお、ここで言う「CloudWatchエージェント」とは、EC2インスタンスに対してインストールするCloudWatchエージェントではなく、Container Insightsのために用意されている専用のエージェントであり、コンテナイメージとして提供されています。
(1) 名前空間を作成する
Kubernetesには「名前空間」(Namespace) の概念があり、名前空間にはKubernetesのシステム関連リソースが配置されている「kube-system」や、ユーザーアプリケーションがデフォルトで配置される「default」などがあります。
ここでは、CloudWatchエージェント専用のNamespace「amazon-cloudwatch」を作成します。
Namespaceを作成するマニフェストが用意されていますので、ダウンロードします。
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/cloudwatch-namespace.yaml
マニフェストファイルの内容 (クリックすると展開します)
cloudwatch-namespace.yaml
# create amazon-cloudwatch namespace
apiVersion: v1
kind: Namespace
metadata:
name: amazon-cloudwatch
labels:
name: amazon-cloudwatch
kubernetes apply
コマンドを使ってNamespaceリソースを作成します。
$ kubectl apply -f cloudwatch-namespace.yaml
namespace/amazon-cloudwatch created
(2) サービスアカウントおよび関連リソースを作成する
「サービスアカウント」(ServiceAccount) とはKubernetes上のユーザーアカウントの一種であり、人が操作するためではなく、アプリケーション・サービスの実行主体となるアカウントです。
(AWSのIAMロールに近い概念です)
こちらもマニフェストが用意されていますので、ダウンロードします。
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/cwagent/cwagent-serviceaccount.yaml
マニフェストファイルの内容 (クリックすると展開します)
cwagent-serviceaccount.yaml
# create cwagent service account and role binding
apiVersion: v1
kind: ServiceAccount
metadata:
name: cloudwatch-agent
namespace: amazon-cloudwatch
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: cloudwatch-agent-role
rules:
- apiGroups: [""]
resources: ["pods", "nodes", "endpoints"]
verbs: ["list", "watch"]
- apiGroups: ["apps"]
resources: ["replicasets"]
verbs: ["list", "watch"]
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["list", "watch"]
- apiGroups: [""]
resources: ["nodes/proxy"]
verbs: ["get"]
- apiGroups: [""]
resources: ["nodes/stats", "configmaps", "events"]
verbs: ["create"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["cwagent-clusterleader"]
verbs: ["get","update"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: cloudwatch-agent-role-binding
subjects:
- kind: ServiceAccount
name: cloudwatch-agent
namespace: amazon-cloudwatch
roleRef:
kind: ClusterRole
name: cloudwatch-agent-role
apiGroup: rbac.authorization.k8s.io
マニフェストでは3つのリソースを作成します。
「ServiceAccount」は、上で説明した通り、CloudWatchエージェントを実行する主体となるアカウントです。
「ClusterRole」は、アカウントに対して与える権限を定義するものです。
メトリクスを取得するために必要な「NodeやPodの情報を参照する権限」などが記述されています。
(Kubernetesの「ClusterRole」はAWSの「ロール」とは意味合いが異なり、どちらかと言うとAWSのIAMポリシーに近い概念です)
「ClusterRoleBinding」は、「ServiceAccount」に対して「ClusterRole」を割り当てるものです。
kubernetes apply
コマンドを使って各リソースを作成します。
$ kubectl apply -f cwagent-serviceaccount.yaml
serviceaccount/cloudwatch-agent created
clusterrole.rbac.authorization.k8s.io/cloudwatch-agent-role created
clusterrolebinding.rbac.authorization.k8s.io/cloudwatch-agent-role-binding created
(3) ConfigMapを作成する
「ConfigMap」とは、Kubernetesのコンテナに対する設定情報などを格納するリソースです。
ConfigMapに格納された設定情報は、コンテナ内から環境変数やファイルなどを経由して参照することができます。
ここでは、CloudWatchエージェントのコンテナに対する設定をConfigMapに記述します。
ConfigMapを作成するマニフェストのテンプレートが用意されていますので、ダウンロードします。
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/cwagent/cwagent-configmap.yaml
マニフェストテンプレートの内容は以下のようになっています。
cwagent-configmap.yaml
# create configmap for cwagent config
apiVersion: v1
data:
# Configuration is in Json format. No matter what configure change you make,
# please keep the Json blob valid.
cwagentconfig.json: |
{
"logs": {
"metrics_collected": {
"kubernetes": {
"cluster_name": "{{cluster_name}}",
"metrics_collection_interval": 60
}
},
"force_flush_interval": 5
}
}
kind: ConfigMap
metadata:
name: cwagentconfig
namespace: amazon-cloudwatch
このマニフェストテンプレートを基に、環境に応じて内容を更新してマニフェストを準備します。
最低限の必要な修正は、11行目の "cluster_name": "{{cluster_name}}"
の部分です。
具体的には {{cluster_name}}
をEKSクラスター名で置き換えます。
今回の例であれば以下のようにします。
"cluster_name": "eks-example"
もしくは、EKSクラスター名の指定を省略することもできます。(その場合は11行目の全体を削除します)
その他の設定項目・設定の記述方法については、下記リンク先を参照してください。
CloudWatch エージェントの ConfigMap を作成する
ConfigMapの編集が終わりましたら、kubernetes apply
コマンドを使ってConfigMapリソースを作成します。
$ kubectl apply -f cwagent-configmap.yaml
configmap/cwagentconfig created
(4) DaemonSetとしてCloudWatchエージェントをデプロイする
「DaemonSet」とは、Kubernetes上でコンテナを実行するワークロードの種類のうちの一つです。
DaemonSetで定義されたコンテナは、各ワーカーノード (EC2インスタンス) 上で常駐型のコンテナとして実行されます。
全てのワーカーノードにおいて、ワーカーノード1台に対してコンテナ1個が配置されます。
Container Insightsでは、各ワーカーノードでCloudWatchエージェントのコンテナが常駐することで、ワーカーノードのメトリクスおよびワーカーノード上で実行されるコンテナのメトリクスを収集します。
DaemonSstを作成するマニフェストが用意されていますので、ダウンロードします。
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/cwagent/cwagent-daemonset.yaml
マニフェストファイルの内容 (クリックすると展開します)
cwagent-daemonset.yaml
# deploy cwagent as daemonset
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: cloudwatch-agent
namespace: amazon-cloudwatch
spec:
selector:
matchLabels:
name: cloudwatch-agent
template:
metadata:
labels:
name: cloudwatch-agent
spec:
containers:
- name: cloudwatch-agent
image: amazon/cloudwatch-agent:1.231221.0
#ports:
# - containerPort: 8125
# hostPort: 8125
# protocol: UDP
resources:
limits:
cpu: 200m
memory: 200Mi
requests:
cpu: 200m
memory: 200Mi
# Please don't change below envs
env:
- name: HOST_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: HOST_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: K8S_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: CI_VERSION
value: "k8s/1.1.0"
# Please don't change the mountPath
volumeMounts:
- name: cwagentconfig
mountPath: /etc/cwagentconfig
- name: rootfs
mountPath: /rootfs
readOnly: true
- name: dockersock
mountPath: /var/run/docker.sock
readOnly: true
- name: varlibdocker
mountPath: /var/lib/docker
readOnly: true
- name: sys
mountPath: /sys
readOnly: true
- name: devdisk
mountPath: /dev/disk
readOnly: true
volumes:
- name: cwagentconfig
configMap:
name: cwagentconfig
- name: rootfs
hostPath:
path: /
- name: dockersock
hostPath:
path: /var/run/docker.sock
- name: varlibdocker
hostPath:
path: /var/lib/docker
- name: sys
hostPath:
path: /sys
- name: devdisk
hostPath:
path: /dev/disk/
terminationGracePeriodSeconds: 60
serviceAccountName: cloudwatch-agent
DaemonSetで定義されるコンテナは、DockerHubのパブリックリポジトリでAWSが公開している amazon/cloudwatch-agent:1.231221.0
というコンテナイメージから起動されます。
マニフェストには env
や volumeMounts
などのパラメータが記述されていますが、変更せずにそのままとします。
kubernetes apply
コマンドを使ってDaemonSetリソースを作成します。
$ kubectl apply -f cwagent-daemonset.yaml
daemonset.apps/cloudwatch-agent created
正常に作成されたことを確認するために、DaemonSetとPodの一覧を表示します。
$ kubectl get daemonsets -n amazon-cloudwatch
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
cloudwatch-agent 2 2 2 2 2 <none> 38s
$ kubectl get pods -n amazon-cloudwatch -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cloudwatch-agent-4427l 1/1 Running 0 96s 192.168.39.252 ip-192-168-36-52.ap-northeast-1.compute.internal <none> <none>
cloudwatch-agent-7p2b2 1/1 Running 0 95s 192.168.25.224 ip-192-168-28-45.ap-northeast-1.compute.internal <none> <none>
各ワーカーノード上でPodが1つずつ実行されていることが確認できます。
もし、Podが起動しない場合や、起動・停止を繰り返す場合には、Podのイベントやログを確認して原因を調べます。
$ kubectl describe pod cloudwatch-agent-XXXXX -n amazon-cloudwatch
$ kubectl logs cloudwatch-agent-XXXXX -n amazon-cloudwatch
例えば、正常に起動した場合のイベントは以下のようになっています。
$ kubectl describe pod cloudwatch-agent-4427l -n amazon-cloudwatch
Name: cloudwatch-agent-4427l
Namespace: amazon-cloudwatch
(中略)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m56s default-scheduler Successfully assigned amazon-cloudwatch/cloudwatch-agent-4427l to ip-192-168-36-52.ap-northeast-1.compute.internal
Normal Pulling 2m56s kubelet, ip-192-168-36-52.ap-northeast-1.compute.internal Pulling image "amazon/cloudwatch-agent:1.231221.0"
Normal Pulled 2m51s kubelet, ip-192-168-36-52.ap-northeast-1.compute.internal Successfully pulled image "amazon/cloudwatch-agent:1.231221.0"
Normal Created 2m51s kubelet, ip-192-168-36-52.ap-northeast-1.compute.internal Created container cloudwatch-agent
Normal Started 2m51s kubelet, ip-192-168-36-52.ap-northeast-1.compute.internal Started container cloudwatch-agent
(5) CloudWatchへメトリクスが出力されることを確認する
DaemonSetをデプロイしてしばらく経つと、CloudWatchエージェントからCloudWatchへメトリクスが出力されるようになります。
マネジメントコンソールでCloudWatchのメトリクスを開いて、「カスタム名前空間」に「ContainerInsights」が表示されていることを確認します。
「ContainerInsights」名前空間の配下には、クラスター、ノード、ポッドなど、Kubernetesの各リソースに関するメトリクスが出力されていることが確認できます。
また、CloudWatchエージェントはメトリクスの他にログも出力します。
ロググループ /aws/containerinsights/(クラスター名)/performance
が作成されていることを確認します。
これらのメトリクス、ログが出力されていれば、CloudWatchエージェントは正常に動作しています。
もし出力されていない場合には、ここまでの手順を見直してください。
(特に、ワーカーノードのIAMロールにポリシーが追加されているか? Podのイベントやログにエラーが無いか? という点を確認すると良いと思います)
ステップ2: ログ収集を行うFluentdを導入する
Container Insightsのログ収集は、オープンソースのログ収集ソフトウェアである「Fluentd」が使われています。
「Fluentd」は様々な形態で提供されていますが、Container InsightsではKubernetes向けに提供されているFluentdコンテナイメージを用います。
導入は以下のページの内容に沿って進めます。
CloudWatch Logs へログを送信する DaemonSet として FluentD をセットアップする - Amazon CloudWatch
(1) 名前空間を作成する
FluentdはCloudWatchエージェント用のNamespace「amazon-cloudwatch」に配置する必要がありますが、ステップ1の手順で既に作成しているため、改めて作成する必要はありません。
(2) ConfigMapを作成する
Fluentdに対して設定を与えるConfigMapを作成します。
マニフェストのテンプレートファイルを以下のように作成します。
cluster-info.yaml
# create configmap for cluster name and aws region for CloudWatch Logs
# need to replace the placeholders {{cluster_name}} and {{region_name}}
apiVersion: v1
data:
cluster.name: {{cluster_name}}
logs.region: {{region_name}}
kind: ConfigMap
metadata:
name: cluster-info
namespace: amazon-cloudwatch
5行目の cluster.name: {{cluster_name}}
と、6行目の logs.region: {{region_name}}
の部分を修正します。
今回の例であれば以下のようにします。
`cluster.name: eks-example`
`logs.region: ap-northeast-1`
kubernetes apply
コマンドを使ってConfigMapリソースを作成します。
$ kubectl apply -f fluentd-configmap.yaml
configmap/cluster-info created
(3) Fluentdをインストールする
ここからは、Fluentdを構成する各種リソースを一気に作成します。
マニフェストが用意されていますので、ダウンロードします。
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/fluentd/fluentd.yaml
マニフェストファイルの内容 (クリックすると展開します)
cloudwatch-fluentd.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluentd
namespace: amazon-cloudwatch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluentd-role
rules:
- apiGroups: [""]
resources:
- namespaces
- pods
- pods/logs
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: fluentd-role-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: fluentd-role
subjects:
- kind: ServiceAccount
name: fluentd
namespace: amazon-cloudwatch
---
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
namespace: amazon-cloudwatch
labels:
k8s-app: fluentd-cloudwatch
data:
fluent.conf: |
@include containers.conf
@include systemd.conf
@include host.conf
<match fluent.**>
@type null
</match>
containers.conf: |
<source>
@type tail
@id in_tail_container_logs
@label @containers
path /var/log/containers/*.log
exclude_path ["/var/log/containers/cloudwatch-agent*", "/var/log/containers/fluentd*"]
pos_file /var/log/fluentd-containers.log.pos
tag *
read_from_head true
<parse>
@type json
time_format %Y-%m-%dT%H:%M:%S.%NZ
</parse>
</source>
<source>
@type tail
@id in_tail_cwagent_logs
@label @cwagentlogs
path /var/log/containers/cloudwatch-agent*
pos_file /var/log/cloudwatch-agent.log.pos
tag *
read_from_head true
<parse>
@type json
time_format %Y-%m-%dT%H:%M:%S.%NZ
</parse>
</source>
<source>
@type tail
@id in_tail_fluentd_logs
@label @fluentdlogs
path /var/log/containers/fluentd*
pos_file /var/log/fluentd.log.pos
tag *
read_from_head true
<parse>
@type json
time_format %Y-%m-%dT%H:%M:%S.%NZ
</parse>
</source>
<label @fluentdlogs>
<filter **>
@type kubernetes_metadata
@id filter_kube_metadata_fluentd
</filter>
<filter **>
@type record_transformer
@id filter_fluentd_stream_transformer
<record>
stream_name ${tag_parts[3]}
</record>
</filter>
<match **>
@type relabel
@label @NORMAL
</match>
</label>
<label @containers>
<filter **>
@type kubernetes_metadata
@id filter_kube_metadata
</filter>
<filter **>
@type record_transformer
@id filter_containers_stream_transformer
<record>
stream_name ${tag_parts[3]}
</record>
</filter>
<filter **>
@type concat
key log
multiline_start_regexp /^\S/
separator ""
flush_interval 5
timeout_label @NORMAL
</filter>
<match **>
@type relabel
@label @NORMAL
</match>
</label>
<label @cwagentlogs>
<filter **>
@type kubernetes_metadata
@id filter_kube_metadata_cwagent
</filter>
<filter **>
@type record_transformer
@id filter_cwagent_stream_transformer
<record>
stream_name ${tag_parts[3]}
</record>
</filter>
<filter **>
@type concat
key log
multiline_start_regexp /^\d{4}[-/]\d{1,2}[-/]\d{1,2}/
separator ""
flush_interval 5
timeout_label @NORMAL
</filter>
<match **>
@type relabel
@label @NORMAL
</match>
</label>
<label @NORMAL>
<match **>
@type cloudwatch_logs
@id out_cloudwatch_logs_containers
region "#{ENV.fetch('REGION')}"
log_group_name "/aws/containerinsights/#{ENV.fetch('CLUSTER_NAME')}/application"
log_stream_name_key stream_name
remove_log_stream_name_key true
auto_create_stream true
<buffer>
flush_interval 5
chunk_limit_size 2m
queued_chunks_limit_size 32
retry_forever true
</buffer>
</match>
</label>
systemd.conf: |
<source>
@type systemd
@id in_systemd_kubelet
@label @systemd
filters [{ "_SYSTEMD_UNIT": "kubelet.service" }]
<entry>
field_map {"MESSAGE": "message", "_HOSTNAME": "hostname", "_SYSTEMD_UNIT": "systemd_unit"}
field_map_strict true
</entry>
path /var/log/journal
<storage>
@type local
persistent true
path /var/log/fluentd-journald-kubelet-pos.json
</storage>
read_from_head true
tag kubelet.service
</source>
<source>
@type systemd
@id in_systemd_kubeproxy
@label @systemd
filters [{ "_SYSTEMD_UNIT": "kubeproxy.service" }]
<entry>
field_map {"MESSAGE": "message", "_HOSTNAME": "hostname", "_SYSTEMD_UNIT": "systemd_unit"}
field_map_strict true
</entry>
path /var/log/journal
<storage>
@type local
persistent true
path /var/log/fluentd-journald-kubeproxy-pos.json
</storage>
read_from_head true
tag kubeproxy.service
</source>
<source>
@type systemd
@id in_systemd_docker
@label @systemd
filters [{ "_SYSTEMD_UNIT": "docker.service" }]
<entry>
field_map {"MESSAGE": "message", "_HOSTNAME": "hostname", "_SYSTEMD_UNIT": "systemd_unit"}
field_map_strict true
</entry>
path /var/log/journal
<storage>
@type local
persistent true
path /var/log/fluentd-journald-docker-pos.json
</storage>
read_from_head true
tag docker.service
</source>
<label @systemd>
<filter **>
@type kubernetes_metadata
@id filter_kube_metadata_systemd
</filter>
<filter **>
@type record_transformer
@id filter_systemd_stream_transformer
<record>
stream_name ${tag}-${record["hostname"]}
</record>
</filter>
<match **>
@type cloudwatch_logs
@id out_cloudwatch_logs_systemd
region "#{ENV.fetch('REGION')}"
log_group_name "/aws/containerinsights/#{ENV.fetch('CLUSTER_NAME')}/dataplane"
log_stream_name_key stream_name
auto_create_stream true
remove_log_stream_name_key true
<buffer>
flush_interval 5
chunk_limit_size 2m
queued_chunks_limit_size 32
retry_forever true
</buffer>
</match>
</label>
host.conf: |
<source>
@type tail
@id in_tail_dmesg
@label @hostlogs
path /var/log/dmesg
pos_file /var/log/dmesg.log.pos
tag host.dmesg
read_from_head true
<parse>
@type syslog
</parse>
</source>
<source>
@type tail
@id in_tail_secure
@label @hostlogs
path /var/log/secure
pos_file /var/log/secure.log.pos
tag host.secure
read_from_head true
<parse>
@type syslog
</parse>
</source>
<source>
@type tail
@id in_tail_messages
@label @hostlogs
path /var/log/messages
pos_file /var/log/messages.log.pos
tag host.messages
read_from_head true
<parse>
@type syslog
</parse>
</source>
<label @hostlogs>
<filter **>
@type kubernetes_metadata
@id filter_kube_metadata_host
</filter>
<filter **>
@type record_transformer
@id filter_containers_stream_transformer_host
<record>
stream_name ${tag}-${record["host"]}
</record>
</filter>
<match host.**>
@type cloudwatch_logs
@id out_cloudwatch_logs_host_logs
region "#{ENV.fetch('REGION')}"
log_group_name "/aws/containerinsights/#{ENV.fetch('CLUSTER_NAME')}/host"
log_stream_name_key stream_name
remove_log_stream_name_key true
auto_create_stream true
<buffer>
flush_interval 5
chunk_limit_size 2m
queued_chunks_limit_size 32
retry_forever true
</buffer>
</match>
</label>
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-cloudwatch
namespace: amazon-cloudwatch
spec:
selector:
matchLabels:
k8s-app: fluentd-cloudwatch
template:
metadata:
labels:
k8s-app: fluentd-cloudwatch
annotations:
configHash: 8915de4cf9c3551a8dc74c0137a3e83569d28c71044b0359c2578d2e0461825
spec:
serviceAccountName: fluentd
terminationGracePeriodSeconds: 30
# Because the image's entrypoint requires to write on /fluentd/etc but we mount configmap there which is read-only,
# this initContainers workaround or other is needed.
# See https://github.com/fluent/fluentd-kubernetes-daemonset/issues/90
initContainers:
- name: copy-fluentd-config
image: busybox
command: ['sh', '-c', 'cp /config-volume/..data/* /fluentd/etc']
volumeMounts:
- name: config-volume
mountPath: /config-volume
- name: fluentdconf
mountPath: /fluentd/etc
- name: update-log-driver
image: busybox
command: ['sh','-c','']
containers:
- name: fluentd-cloudwatch
image: fluent/fluentd-kubernetes-daemonset:v1.7.3-debian-cloudwatch-1.0
env:
- name: REGION
valueFrom:
configMapKeyRef:
name: cluster-info
key: logs.region
- name: CLUSTER_NAME
valueFrom:
configMapKeyRef:
name: cluster-info
key: cluster.name
- name: CI_VERSION
value: "k8s/1.1.0"
resources:
limits:
memory: 400Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: config-volume
mountPath: /config-volume
- name: fluentdconf
mountPath: /fluentd/etc
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: runlogjournal
mountPath: /run/log/journal
readOnly: true
- name: dmesg
mountPath: /var/log/dmesg
readOnly: true
volumes:
- name: config-volume
configMap:
name: fluentd-config
- name: fluentdconf
emptyDir: {}
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: runlogjournal
hostPath:
path: /run/log/journal
- name: dmesg
hostPath:
path: /var/log/dmesg
このマニフェストには以下のリソースが含まれています。
- ServiceAccount「fluentd」
- ClusterRole「fluentd-role」
- ClusterRoleBinding「fluentd-role-binding」
- ConfigMap「fluentd-config」(*)
- DaemonSet「fluentd-cloudwatch」
(*) このConfigMapは、前の手順で作成したConfigMapとは別物です。Fluentdの動作をカスタマイズする様々な設定が含まれますが、今回はデフォルト設定のまま進めます。
これらのリソースの役割は、CloudWatchエージェントの時とほぼ同様です。
ServiceAccount、ClusterRole、ClusterRoleBindingによってFluentdの動作に必要な権限を設定して、DaemonSetによってFluentdを常駐型コンテナとして実行します。
kubernetes apply
コマンドを使って各リソースを作成します。
$ kubectl apply -f fluentd.yaml
serviceaccount/fluentd created
clusterrole.rbac.authorization.k8s.io/fluentd-role created
clusterrolebinding.rbac.authorization.k8s.io/fluentd-role-binding created
configmap/fluentd-config created
daemonset.apps/fluentd-cloudwatch created
正常に作成されたことを確認するために、DaemonSetとPodの一覧を表示します。
$ kubectl get daemonsets -n amazon-cloudwatch
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
cloudwatch-agent 2 2 2 2 2 <none> 71m
fluentd-cloudwatch 2 2 2 2 2 <none> 85s
$ kubectl get pods -n amazon-cloudwatch -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cloudwatch-agent-4427l 1/1 Running 0 71m 192.168.39.252 ip-192-168-36-52.ap-northeast-1.compute.internal <none> <none>
cloudwatch-agent-7p2b2 1/1 Running 0 71m 192.168.25.224 ip-192-168-28-45.ap-northeast-1.compute.internal <none> <none>
fluentd-cloudwatch-g9jm8 1/1 Running 0 105s 192.168.52.175 ip-192-168-36-52.ap-northeast-1.compute.internal <none> <none>
fluentd-cloudwatch-gdv4b 1/1 Running 0 105s 192.168.8.71 ip-192-168-28-45.ap-northeast-1.compute.internal <none> <none>
ステップ1で作成したCloudWatchエージェントのDaemonSetやPodに加えて、FluentdのDaemonSetとPodが作成されていることが確認できます。
(4) CloudWatchへログが出力されることを確認する
Fluentdをデプロイしてしばらく経つと、CloudWatchエージェントからCloudWatchへメトリクスが出力されるようになります。
マネジメントコンソールでCloudWatchのログを開いて、以下の3つのロググループが作成されていることを確認します。
/aws/containerinsights/(クラスター名)/application
/aws/containerinsights/(クラスター名)/host
/aws/containerinsights/(クラスター名)/dataplane
これらのログが出力されていれば、Fluentdは正常に動作しています。
Container Insightsのダッシュボードを表示する
CloudWatchのトップ画面を表示します。
「概要」プルダウンをクリックして「Container Insights」を選択します。
Container Insightsのダッシュボードが表示され、EKSクラスターのメトリクスデータがグラフ表示されていることを確認します。
おわりに
今回ご紹介した手順を一通り試してみることにより、Container InsightsがどのようなKubernetesのリソースによって動作しているのかを理解することができるのではないかと思います。
Container Insightsを素早く導入するには「クイックスタート」の手順に沿って行うのが良いでしょう。
クイックスタートのマニフェストを使う際に、今回の内容を参考にしてマニフェストをカスタマイズすることも可能かと思います。