Container Insights の EKS Fargate サポートを試す

AWS Distro for OpenTelemetry を使用した Container Insights の EKS Fargate サポートを試すメモ。

原理的には、API Server をプロキシとして利用して cAdvisor のメトリクスを取得するもので、以前から Container Insights の Prometheus サポートを使ってできたことと同じに思える。

以前試したのがこちら。

クラスターの作成

1.21 のクラスターをノードなしで作成する。

CLUSTER_NAME="container-insights-fargate"
cat << EOF > cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: ${CLUSTER_NAME}
  region: ap-northeast-1
  version: "1.21"
vpc:
  cidr: "10.0.0.0/16"

availabilityZones:
  - ap-northeast-1a
  - ap-northeast-1c

cloudWatch:
  clusterLogging:
    enableTypes: ["*"]

iam:
  withOIDC: true
EOF
eksctl create cluster -f cluster.yaml

ノードを作成する。

cat << EOF > managed-ng-1.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: ${CLUSTER_NAME}
  region: ap-northeast-1

managedNodeGroups:
  - name: managed-ng-1
    minSize: 2
    maxSize: 10
    desiredCapacity: 2
    privateNetworking: true
    iam:
      attachPolicyARNs:
        - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
EOF
eksctl create nodegroup -f managed-ng-1.yaml

Admin ロールにも権限をつけておく。

USER_NAME="Admin:{{SessionName}}"
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/Admin"
eksctl create iamidentitymapping --cluster ${CLUSTER_NAME} --arn ${ROLE_ARN} --username ${USER_NAME} --group system:masters

Fargate プロファイルの作成

Fargate プロファイルを 2 つ作成する。

eksctl create fargateprofile \
    --cluster  ${CLUSTER_NAME} \
    --name fargate-container-insights \
    --namespace fargate-container-insights
eksctl create fargateprofile \
    --cluster  ${CLUSTER_NAME} \
    --name applications \
    --namespace golang

ADOT コレクターのデプロイ

ブログ記事のヘルパースクリプトを実行して ServiceAccount と IAM ロールを作成する。

#!/bin/bash
CLUSTER_NAME="container-insights-fargate"
REGION="ap-northeast-1"
SERVICE_ACCOUNT_NAMESPACE=fargate-container-insights
SERVICE_ACCOUNT_NAME=adot-collector
SERVICE_ACCOUNT_IAM_ROLE=EKS-Fargate-ADOT-ServiceAccount-Role
SERVICE_ACCOUNT_IAM_POLICY=arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy

eksctl utils associate-iam-oidc-provider \
--cluster=$CLUSTER_NAME \
--approve

eksctl create iamserviceaccount \
--cluster=$CLUSTER_NAME \
--region=$REGION \
--name=$SERVICE_ACCOUNT_NAME \
--namespace=$SERVICE_ACCOUNT_NAMESPACE \
--role-name=$SERVICE_ACCOUNT_IAM_ROLE \
--attach-policy-arn=$SERVICE_ACCOUNT_IAM_POLICY \
--approve

ADOT コレクターをデプロイする。この手順のガイドはここマニフェストここにある。

cat << EOF > adot-collector.yaml
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: adotcol-admin-role
rules:
  - apiGroups: [""]
    resources:
      - nodes
      - nodes/proxy
      - nodes/metrics
      - services
      - endpoints
      - pods
      - pods/proxy
    verbs: ["get", "list", "watch"]
  - nonResourceURLs: [ "/metrics/cadvisor"]
    verbs: ["get", "list", "watch"]

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: adotcol-admin-role-binding
subjects:
  - kind: ServiceAccount
    name: adot-collector
    namespace: fargate-container-insights
roleRef:
  kind: ClusterRole
  name: adotcol-admin-role
  apiGroup: rbac.authorization.k8s.io

# collector configuration section
# update `ClusterName=YOUR-EKS-CLUSTER-NAME` in the env variable OTEL_RESOURCE_ATTRIBUTES
# update `region=YOUR-AWS-REGION` in the emfexporter with the name of the AWS Region where you want to collect Container Insights metrics.
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: adot-collector-config
  namespace: fargate-container-insights
  labels:
    app: aws-adot
    component: adot-collector-config
data:
  adot-collector-config: |
    receivers:
      prometheus:
        config:
          global:
            scrape_interval: 1m
            scrape_timeout: 40s

          scrape_configs:
          - job_name: 'kubelets-cadvisor-metrics'
            sample_limit: 10000
            scheme: https

            kubernetes_sd_configs:
            - role: node
            tls_config:
              ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

            relabel_configs:
              - action: labelmap
                regex: __meta_kubernetes_node_label_(.+)
                # Only for Kubernetes ^1.7.3.
                # See: https://github.com/prometheus/prometheus/issues/2916
              - target_label: __address__
                # Changes the address to Kube API server's default address and port
                replacement: kubernetes.default.svc:443
              - source_labels: [__meta_kubernetes_node_name]
                regex: (.+)
                target_label: __metrics_path__
                # Changes the default metrics path to kubelet's proxy cadvdisor metrics endpoint
                replacement: /api/v1/nodes/$${1}/proxy/metrics/cadvisor
            metric_relabel_configs:
              # extract readable container/pod name from id field
              - action: replace
                source_labels: [id]
                regex: '^/machine\.slice/machine-rkt\\x2d([^\\]+)\\.+/([^/]+)\.service$'
                target_label: rkt_container_name
                replacement: '$${2}-$${1}'
              - action: replace
                source_labels: [id]
                regex: '^/system\.slice/(.+)\.service$'
                target_label: systemd_service_name
                replacement: '$${1}'
    processors:
      # rename labels which apply to all metrics and are used in metricstransform/rename processor
      metricstransform/label_1:
        transforms:
          - include: .*
            match_type: regexp
            action: update
            operations:
              - action: update_label
                label: name
                new_label: container_id
              - action: update_label
                label: kubernetes_io_hostname
                new_label: NodeName
              - action: update_label
                label: eks_amazonaws_com_compute_type
                new_label: LaunchType

      # rename container and pod metrics which we care about.
      # container metrics are renamed to `new_container_*` to differentiate them with unused container metrics
      metricstransform/rename:
        transforms:
          - include: container_spec_cpu_quota
            new_name: new_container_cpu_limit_raw
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_spec_cpu_shares
            new_name: new_container_cpu_request
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_cpu_usage_seconds_total
            new_name: new_container_cpu_usage_seconds_total
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_spec_memory_limit_bytes
            new_name: new_container_memory_limit
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_memory_cache
            new_name: new_container_memory_cache
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_memory_max_usage_bytes
            new_name: new_container_memory_max_usage
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_memory_usage_bytes
            new_name: new_container_memory_usage
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_memory_working_set_bytes
            new_name: new_container_memory_working_set
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_memory_rss
            new_name: new_container_memory_rss
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_memory_swap
            new_name: new_container_memory_swap
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_memory_failcnt
            new_name: new_container_memory_failcnt
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_memory_failures_total
            new_name: new_container_memory_hierarchical_pgfault
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "hierarchy"}
          - include: container_memory_failures_total
            new_name: new_container_memory_hierarchical_pgmajfault
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "hierarchy"}
          - include: container_memory_failures_total
            new_name: new_container_memory_pgfault
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "container"}
          - include: container_memory_failures_total
            new_name: new_container_memory_pgmajfault
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "container"}
          - include: container_fs_limit_bytes
            new_name: new_container_filesystem_capacity
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          - include: container_fs_usage_bytes
            new_name: new_container_filesystem_usage
            action: insert
            match_type: regexp
            experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
          # POD LEVEL METRICS
          - include: container_spec_cpu_quota
            new_name: pod_cpu_limit_raw
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
          - include: container_spec_cpu_shares
            new_name: pod_cpu_request
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
          - include: container_cpu_usage_seconds_total
            new_name: pod_cpu_usage_seconds_total
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
          - include: container_spec_memory_limit_bytes
            new_name: pod_memory_limit
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
          - include: container_memory_cache
            new_name: pod_memory_cache
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
          - include: container_memory_max_usage_bytes
            new_name: pod_memory_max_usage
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
          - include: container_memory_usage_bytes
            new_name: pod_memory_usage
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
          - include: container_memory_working_set_bytes
            new_name: pod_memory_working_set
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
          - include: container_memory_rss
            new_name: pod_memory_rss
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
          - include: container_memory_swap
            new_name: pod_memory_swap
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
          - include: container_memory_failcnt
            new_name: pod_memory_failcnt
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
          - include: container_memory_failures_total
            new_name: pod_memory_hierarchical_pgfault
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "hierarchy"}
          - include: container_memory_failures_total
            new_name: pod_memory_hierarchical_pgmajfault
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "hierarchy"}
          - include: container_memory_failures_total
            new_name: pod_memory_pgfault
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "container"}
          - include: container_memory_failures_total
            new_name: pod_memory_pgmajfault
            action: insert
            match_type: regexp
            experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "container"}
          - include: container_network_receive_bytes_total
            new_name: pod_network_rx_bytes
            action: insert
            match_type: regexp
            experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
          - include: container_network_receive_packets_dropped_total
            new_name: pod_network_rx_dropped
            action: insert
            match_type: regexp
            experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
          - include: container_network_receive_errors_total
            new_name: pod_network_rx_errors
            action: insert
            match_type: regexp
            experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
          - include: container_network_receive_packets_total
            new_name: pod_network_rx_packets
            action: insert
            match_type: regexp
            experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
          - include: container_network_transmit_bytes_total
            new_name: pod_network_tx_bytes
            action: insert
            match_type: regexp
            experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
          - include: container_network_transmit_packets_dropped_total
            new_name: pod_network_tx_dropped
            action: insert
            match_type: regexp
            experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
          - include: container_network_transmit_errors_total
            new_name: pod_network_tx_errors
            action: insert
            match_type: regexp
            experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
          - include: container_network_transmit_packets_total
            new_name: pod_network_tx_packets
            action: insert
            match_type: regexp
            experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}

      # filter out only renamed metrics which we care about
      filter:
        metrics:
          include:
            match_type: regexp
            metric_names:
              - new_container_.*
              - pod_.*

      # convert cumulative sum datapoints to delta
      cumulativetodelta:
        metrics:
          - new_container_cpu_usage_seconds_total
          - pod_cpu_usage_seconds_total
          - pod_memory_pgfault
          - pod_memory_pgmajfault
          - pod_memory_hierarchical_pgfault
          - pod_memory_hierarchical_pgmajfault
          - pod_network_rx_bytes
          - pod_network_rx_dropped
          - pod_network_rx_errors
          - pod_network_rx_packets
          - pod_network_tx_bytes
          - pod_network_tx_dropped
          - pod_network_tx_errors
          - pod_network_tx_packets
          - new_container_memory_pgfault
          - new_container_memory_pgmajfault
          - new_container_memory_hierarchical_pgfault
          - new_container_memory_hierarchical_pgmajfault

      # convert delta to rate
      deltatorate:
        metrics:
          - new_container_cpu_usage_seconds_total
          - pod_cpu_usage_seconds_total
          - pod_memory_pgfault
          - pod_memory_pgmajfault
          - pod_memory_hierarchical_pgfault
          - pod_memory_hierarchical_pgmajfault
          - pod_network_rx_bytes
          - pod_network_rx_dropped
          - pod_network_rx_errors
          - pod_network_rx_packets
          - pod_network_tx_bytes
          - pod_network_tx_dropped
          - pod_network_tx_errors
          - pod_network_tx_packets
          - new_container_memory_pgfault
          - new_container_memory_pgmajfault
          - new_container_memory_hierarchical_pgfault
          - new_container_memory_hierarchical_pgmajfault

      experimental_metricsgeneration/1:
        rules:
          - name: pod_network_total_bytes
            unit: Bytes/Second
            type: calculate
            metric1: pod_network_rx_bytes
            metric2: pod_network_tx_bytes
            operation: add
          - name: pod_memory_utilization_over_pod_limit
            unit: Percent
            type: calculate
            metric1: pod_memory_working_set
            metric2: pod_memory_limit
            operation: percent
          - name: pod_cpu_usage_total
            unit: Millicore
            type: scale
            metric1: pod_cpu_usage_seconds_total
            operation: multiply
            # core to millicore: multiply by 1000
            # millicore seconds to millicore nanoseconds: multiply by 10^9
            scale_by: 1000
          - name: pod_cpu_limit
            unit: Millicore
            type: scale
            metric1: pod_cpu_limit_raw
            operation: divide
            scale_by: 100

      experimental_metricsgeneration/2:
        rules:
          - name: pod_cpu_utilization_over_pod_limit
            type: calculate
            unit: Percent
            metric1: pod_cpu_usage_total
            metric2: pod_cpu_limit
            operation: percent

      # add `Type` and rename metrics and labels
      metricstransform/label_2:
        transforms:
          - include: pod_.*
            match_type: regexp
            action: update
            operations:
              - action: add_label
                new_label: Type
                new_value: "Pod"
          - include: new_container_.*
            match_type: regexp
            action: update
            operations:
              - action: add_label
                new_label: Type
                new_value: Container
          - include: .*
            match_type: regexp
            action: update
            operations:
              - action: update_label
                label: namespace
                new_label: Namespace
              - action: update_label
                label: pod
                new_label: PodName
          - include: ^new_container_(.*)$$
            match_type: regexp
            action: update
            new_name: container_$$1

      # add cluster name from env variable and EKS metadata
      resourcedetection:
        detectors: [env, eks]

      batch:
        timeout: 60s

    # only pod level metrics in metrics format, details in https://aws-otel.github.io/docs/getting-started/container-insights/eks-fargate
    exporters:
      awsemf:
        log_group_name: '/aws/containerinsights/{ClusterName}/performance'
        log_stream_name: '{PodName}'
        namespace: 'ContainerInsights'
        region: ap-northeast-1
        resource_to_telemetry_conversion:
          enabled: true
        eks_fargate_container_insights_enabled: true
        parse_json_encoded_attr_values: ["kubernetes"]
        dimension_rollup_option: NoDimensionRollup
        metric_declarations:
          - dimensions: [ [ClusterName, LaunchType], [ClusterName, Namespace, LaunchType], [ClusterName, Namespace, PodName, LaunchType]]
            metric_name_selectors:
              - pod_cpu_utilization_over_pod_limit
              - pod_cpu_usage_total
              - pod_cpu_limit
              - pod_memory_utilization_over_pod_limit
              - pod_memory_working_set
              - pod_memory_limit
              - pod_network_rx_bytes
              - pod_network_tx_bytes

    extensions:
      health_check:

    service:
      pipelines:
        metrics:
          receivers: [prometheus]
          processors: [metricstransform/label_1, resourcedetection, metricstransform/rename, filter, cumulativetodelta, deltatorate, experimental_metricsgeneration/1, experimental_metricsgeneration/2, metricstransform/label_2, batch]
          exporters: [awsemf]
      extensions: [health_check]

# configure the service and the collector as a StatefulSet
---
apiVersion: v1
kind: Service
metadata:
  name: adot-collector-service
  namespace: fargate-container-insights
  labels:
    app: aws-adot
    component: adot-collector
spec:
  ports:
    - name: metrics # default endpoint for querying metrics.
      port: 8888
  selector:
    component: adot-collector
  type: ClusterIP

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: adot-collector
  namespace: fargate-container-insights
  labels:
    app: aws-adot
    component: adot-collector
spec:
  selector:
    matchLabels:
      app: aws-adot
      component: adot-collector
  serviceName: adot-collector-service
  template:
    metadata:
      labels:
        app: aws-adot
        component: adot-collector
    spec:
      serviceAccountName: adot-collector
      securityContext:
        fsGroup: 65534
      containers:
        - image: amazon/aws-otel-collector:v0.15.1
          name: adot-collector
          imagePullPolicy: Always
          command:
            - "/awscollector"
            - "--config=/conf/adot-collector-config.yaml"
          env:
            - name: OTEL_RESOURCE_ATTRIBUTES
              value: "ClusterName=container-insights-fargate"
          resources:
            limits:
              cpu: 2
              memory: 2Gi
            requests:
              cpu: 200m
              memory: 400Mi
          volumeMounts:
            - name: adot-collector-config-volume
              mountPath: /conf
      volumes:
        - configMap:
            name: adot-collector-config
            items:
              - key: adot-collector-config
                path: adot-collector-config.yaml
          name: adot-collector-config-volume
---
EOF
$ k apply -f adot-collector.yaml
clusterrole.rbac.authorization.k8s.io/adotcol-admin-role created
clusterrolebinding.rbac.authorization.k8s.io/adotcol-admin-role-binding created
configmap/adot-collector-config created
service/adot-collector-service created
statefulset.apps/adot-collector created

サンプルアプリのデプロイ

サンプルアプリをデプロイする。

cat << EOF > samle-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  namespace: golang
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp
      role: webapp-service
  template:
    metadata:
      labels:
        app: webapp
        role: webapp-service             
    spec: 
      containers:          
        - name: go  
          image: public.ecr.aws/awsvijisarathy/prometheus-webapp:latest
          imagePullPolicy: Always   
          resources:
            requests:
              cpu: "256m"
              memory: "512Mi"
            limits:
              cpu: "512m"
              memory: "1024Mi"            
EOF
$ k create ns golang
namespace/golang created
$ k apply -f samle-deploy.yaml
deployment.apps/webapp created

Pod とノードを確認する。

$ k get po -A
NAMESPACE                    NAME                       READY   STATUS    RESTARTS   AGE
fargate-container-insights   adot-collector-0           1/1     Running   0          27h
golang                       webapp-8b785b667-hcrts     1/1     Running   0          27h
golang                       webapp-8b785b667-v6gfk     1/1     Running   0          27h
kube-system                  aws-node-vxwcs             1/1     Running   0          31h
kube-system                  aws-node-xmcfb             1/1     Running   0          31h
kube-system                  coredns-76f4967988-qb6gd   1/1     Running   0          31h
kube-system                  coredns-76f4967988-sdlnc   1/1     Running   0          31h
kube-system                  kube-proxy-g7m2g           1/1     Running   0          31h
kube-system                  kube-proxy-tf7dq           1/1     Running   0          31h
$ k get no
NAME                                                      STATUS   ROLES    AGE   VERSION
fargate-ip-10-0-103-250.ap-northeast-1.compute.internal   Ready    <none>   27h   v1.21.2-eks-06eac09
fargate-ip-10-0-80-22.ap-northeast-1.compute.internal     Ready    <none>   27h   v1.21.2-eks-06eac09
fargate-ip-10-0-92-178.ap-northeast-1.compute.internal    Ready    <none>   27h   v1.21.2-eks-06eac09
ip-10-0-118-159.ap-northeast-1.compute.internal           Ready    <none>   31h   v1.21.5-eks-9017834
ip-10-0-93-77.ap-northeast-1.compute.internal             Ready    <none>   31h   v1.21.5-eks-9017834

メトリクスの確認

パフォーマンスログを確認する。

f:id:sotoiwa:20220301180331p:plain

ログイベントでは以下のように EMF のログが確認できる。

{
    "ClusterName": "container-insights-fargate",
    "LaunchType": "fargate",
    "Namespace": "golang",
    "NodeName": "ip-10-0-103-250.ap-northeast-1.compute.internal",
    "PodName": "webapp-8b785b667-hcrts",
    "Type": "Pod",
    "_aws": {
        "CloudWatchMetrics": [
            {
                "Namespace": "ContainerInsights",
                "Dimensions": [
                    [
                        "ClusterName",
                        "LaunchType"
                    ],
                    [
                        "ClusterName",
                        "LaunchType",
                        "Namespace"
                    ],
                    [
                        "ClusterName",
                        "LaunchType",
                        "Namespace",
                        "PodName"
                    ]
                ],
                "Metrics": [
                    {
                        "Name": "pod_cpu_usage_total",
                        "Unit": "Millicore"
                    },
                    {
                        "Name": "pod_cpu_utilization_over_pod_limit",
                        "Unit": "Percent"
                    }
                ]
            }
        ],
        "Timestamp": 1646124298710
    },
    "beta_kubernetes_io_arch": "amd64",
    "beta_kubernetes_io_os": "linux",
    "cloud.platform": "aws_eks",
    "cloud.provider": "aws",
    "cpu": "total",
    "failure_domain_beta_kubernetes_io_region": "ap-northeast-1",
    "failure_domain_beta_kubernetes_io_zone": "ap-northeast-1c",
    "host.name": "fargate-ip-10-0-103-250.ap-northeast-1.compute.internal",
    "id": "/kubepods/burstable/pod9e99f29e-5e25-40c1-aa39-35e198dd0997",
    "instance": "fargate-ip-10-0-103-250.ap-northeast-1.compute.internal",
    "job": "kubelets-cadvisor-metrics",
    "kubernetes": {
        "host": "ip-10-0-103-250.ap-northeast-1.compute.internal",
        "namespace_name": "golang",
        "pod_name": "webapp-8b785b667-hcrts"
    },
    "kubernetes_io_arch": "amd64",
    "kubernetes_io_os": "linux",
    "opencensus.resourcetype": "cloud",
    "pod_cpu_usage_seconds_total": 0.00044769273935354483,
    "pod_cpu_usage_total": 0.4476927393535448,
    "pod_cpu_utilization_over_pod_limit": 0.08953854787070896,
    "port": "",
    "scheme": "https",
    "service.name": "kubelets-cadvisor-metrics",
    "topology_kubernetes_io_region": "ap-northeast-1",
    "topology_kubernetes_io_zone": "ap-northeast-1c"
}

EMF が含まれないログもあり、なぜ EMF が含まれるものとそうでないものがあるのかが理解できていない。

{
    "ClusterName": "container-insights-fargate",
    "LaunchType": "fargate",
    "Namespace": "golang",
    "NodeName": "ip-10-0-103-250.ap-northeast-1.compute.internal",
    "PodName": "webapp-8b785b667-hcrts",
    "Type": "Pod",
    "beta_kubernetes_io_arch": "amd64",
    "beta_kubernetes_io_os": "linux",
    "cloud.platform": "aws_eks",
    "cloud.provider": "aws",
    "failure_domain_beta_kubernetes_io_region": "ap-northeast-1",
    "failure_domain_beta_kubernetes_io_zone": "ap-northeast-1c",
    "failure_type": "pgfault",
    "host.name": "fargate-ip-10-0-103-250.ap-northeast-1.compute.internal",
    "id": "/kubepods/burstable/pod9e99f29e-5e25-40c1-aa39-35e198dd0997",
    "instance": "fargate-ip-10-0-103-250.ap-northeast-1.compute.internal",
    "job": "kubelets-cadvisor-metrics",
    "kubernetes": {
        "host": "ip-10-0-103-250.ap-northeast-1.compute.internal",
        "namespace_name": "golang",
        "pod_name": "webapp-8b785b667-hcrts"
    },
    "kubernetes_io_arch": "amd64",
    "kubernetes_io_os": "linux",
    "opencensus.resourcetype": "cloud",
    "pod_memory_hierarchical_pgfault": 0,
    "port": "",
    "scheme": "https",
    "scope": "hierarchy",
    "service.name": "kubelets-cadvisor-metrics",
    "topology_kubernetes_io_region": "ap-northeast-1",
    "topology_kubernetes_io_zone": "ap-northeast-1c"
}

メトリクスとしても Pod 名毎に取得されている。

f:id:sotoiwa:20220301180402p:plain

自動ダッシュボードでもメトリクスが確認できる。ノードのメトリクスはとれていない。

f:id:sotoiwa:20220301180417p:plain

やはり、やっていることは以前から Container Insights の Prometheus サポートを使えばできたことと同じだが、CloudWatch エージェントではなく ADOT コレクターを使用して、かつ適切なセットアップが提供されているということかと思う。

読書メモ: 「本の読み方」で人生が思い通りになる 読書革命

本をたくさん読んで学びたいが、そのためにはまず読書の効率をあげた方がよいと考えており、読書術系の本を読んでいるシリーズ。

205 ページ、平均読書時間は 2 時間 41 分の本。自分の場合は 5 時間くらいかかった。

全体的な感想

非常に勉強家で努力家の著者による本で、1 日 2 冊の本を読んで YouTube 図書館でアウトプットしているらしい。読書法にとどまらず、自己啓発だったりビジネスパーソンとしての姿勢のような内容も多く、よい本であった。ちきりんさんの本を読んでいるようで、主張にはその影響が感じられる。最後におすすめの 30 冊の本が書いてあり、今後の読書の参考にしたい。本の中の主張に合わせて、この本の内容を 3 つのポイントに要約してみる。

3 つに要約

  1. 格差の原因は読書をするかしないかにあり、読書を積み重ねて「思考力を鍛える」ことができ、ビジネスを初めとして人生をよりよくできる
  2. パレートの法則により、本の内容の 20 % が理解できれば、80 % の内容が理解できるので、全部を読む必要はなく「断捨離読み」をする
  3. 本を読んだ内容を忘れないためには、受け身ではない能動的な姿勢が大事であり、アウトプットすることが大事である

気になった部分の引用

私の読書法の主な特徴は3点です。

・1冊の本を1回だけでなく、それぞれ違う方法で4回読む

・1冊の20%を読むことで本全体の80%を理解する

・読んだ内容を誰かに伝える「アウトプット」を重視する

著者の読書法をまとめるとこの通り。4 回とは、「予測読み」「断捨離読み」「記者読み」「要約読み」で、このそれぞれのステップが、ビジネスでも必要であり、思考力を鍛えることができる。例えば「断捨離読み」によって取捨選択や優先順位付けの能力が鍛えられる。

自分の「思考の軸」を鍛えるために最適な本を選ぶための第一歩は同一ジャンルの本を4冊買うことです。

同じジャンルの本を続けて 4 冊読むことを進めている。理由としては、同じ内容を続けて読んだ方が繰り返しの効果で忘れにくいことが書かれていた。同じジャンルの本をたくさん読むというのは「コンサル一年目が学ぶこと」でも紹介されていたし、藤沢数希氏も書かれていた。実践しやすいので取り入れたい。

一方、私の読書法で必要となる2W1Hは、What(なにを)、Why(なぜ)、How(どのように)の3つです。これを意識して本を読めば、著者の問題意識やその解決法についてリーチしやすくなります。

あまりこういったことを意識して読んでいなかったので、意識したい。

1冊をしっかり読むことも大切だと思います。私もそのような読書を否定するつもりはありません。しかし、このようにして「自分に必要な部分を選んで読む」「それをしっかりと自分のものにする」ほうが、1冊読んでもその内容をほとんど覚えていないよりも、役に立つ読書と言えるのではないでしょうか。

何を読むかよりも、何を読まないかのほうが大事です。80%を捨てても20%を理解できれば80点取れると思って、勇気を出して実践してみてください。

どうしても全部読んでしまいたくなり、「断捨離読み」は難しいと感じるが、結局覚えていないのであれば、必要なところをちゃんとものにする方が確かに大事である。とはいえ読まないのは難しいとやはり思うので、重要でなさそうなところは、「遅読家のための読書術」に書いてあった通りフローとして聞き流すのがよいと思う。

読書メモ: Vol.1 賃貸か購入か キンドル・リノベシリーズ

以前ブログや本を読んだりしていたし、最近はよく Voicy を聞かせてもらっているちきりんさんの KDP の本。自分はずっと賃貸に住んでいるが、もう 40 過ぎなのでまわりには家を買っている人のほうが多そうで、どうなのだろうか、と思い読んだ。

最初に結論を書けば、居住用の不動産を「今、買うべき」と言えるのは、次の3つの条件をすべて満たしている人、というのが私の考えです。

1.経済的に無理なく買える状況にある

2.賃貸物件では実現できない「購入したい積極的な理由」が存在する

3.自分や家族のライフプランがある程度、固まっている

主張はここに集約されており、明確である。自分の場合は 2 も足りないし、3 についても今の会社をずっと続けていける気もあまりしていないので、まだかなと感じた。

築25年のマンションのお風呂はどうでしょう?そろそろ取り替えたいと思うほど老朽化していませんか?

マンションでも戸建てでも、修繕が必要なこととそのための積み立てなど準備が必要なことを説明している部分だが、自分が子どもの頃にリフォームした実家のユニットバスや洗面台が、今はものすごく汚くなってしまっており、まさにそうだなと感じた。まだあまり考えたくないけれど、将来、親が亡くなったときに実家をどうしたいのかについて、親の意見を聞いておかなければならないなとも感じた。

家を買うなら「賃貸物件ではぜったい実現できない、購入したい積極的な理由」が必要なのです。

この本で繰り返し述べられている主張はこれであるが、自分の場合、今後 10 年以内にはもう少し広い家が必要になるくらいなので、それが賃貸でも実現できそうなのかを確認する必要があるかなと思う。

「買う方が得だから」と言って自分の家を買う人は、ここがごっちゃになっています。投資なら「得か損か」で決めればよいですが、自分の住む家は経済的な理由だけで選ぶものではありません。

理解していただきたいのは「自分が住むための家を買うのは、不動産投資ではない」ということです。

家を買った人で、たまたま買い換えたけど値上がりしていて得をしたという人の話を聞くとうらやましく思ってしまうが、そう考えないほうがよい。

私がお勧めする「中古マンションの選び方」のひとつは、「自分と価値観や生活スタイル、家族構成が似た人が多く住むマンションを選ぶ」ということです。

まとめれば、集合住宅であるマンションでは「最大のリスクは、他の住民」だというのが、20年間、区分所有者としてマンションに住んだ私の結論です。

これは確かにその通りと思う。一方、同じような住民だと一斉に高齢化するので、ユーカリヶ丘の話のように、世代などいろいろな住民にばらけていた方がいいような気もする。

まずは賃貸に出されている部屋を借りて住み、管理状態が気に入れば購入というのは、中古マンション購入方法としてとてもリスクの低い、いい方法だと思います。

これももしマンションを買うなら実践してみたいと思う。戸建てで借りられるなら借りてみた方がよいとも述べられている。分譲マンションであっても、けっこう賃貸に出されているので借りてみれば、というのはなるほどと思った。

まとめると、さすがちきりんさんなので大変に分かり易かった。75 ページの短い本のはずだが読むのに 1 時間半くらいはかかった。

非 root ユーザーで Pod (コンテナ) を実行したときのケーパビリティー

この記事の続きで、非 root ユーザーの場合のケーパビリティーを EKS で確認したメモ。

準備

ノードのバージョンを確認する。

$ k get node -o wide
NAME                                                 STATUS   ROLES    AGE   VERSION               INTERNAL-IP       EXTERNAL-IP   OS-IMAGE         KERNEL-VERSION                CONTAINER-RUNTIME
ip-192-168-175-232.ap-northeast-1.compute.internal   Ready    <none>   43d   v1.21.5-eks-bc4871b   192.168.175.232   <none>        Amazon Linux 2   5.4.162-86.275.amzn2.x86_64   docker://20.10.7
ip-192-168-200-81.ap-northeast-1.compute.internal    Ready    <none>   43d   v1.21.5-eks-bc4871b   192.168.200.81    <none>        Amazon Linux 2   5.4.162-86.275.amzn2.x86_64   docker://20.10.7

ランタイムは Containerd ではなく Docker。

イメージを作る。

cat << EOF > Dockerfile
FROM ubuntu
RUN apt-get update && apt-get install -y \
    libcap-ng-utils
EOF
docker build -t pscap-ubuntu .

イメージを Docker Hub にプッシュする。

docker tag pscap-ubuntu sotosugi/pscap-ubuntu
docker push sotosugi/pscap-ubuntu

root ユーザー

EKS で実行する。

cat << EOF > pod1.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: pod1
  name: pod1
spec:
  containers:
  - args:
    - sleep
    - "3600"
    image: sotosugi/pscap-ubuntu
    name: pod1
EOF
k apply -f pod1.yaml

Pod の中から確認する。

$ k get po
NAME   READY   STATUS    RESTARTS   AGE
pod1   1/1     Running   0          3s
sotosugi@a483e7dfd6ad:~/workspace/2020/hisys/20220127_Capabliity
$ k exec -it pod1 -- /bin/bash
root@pod1:/# pscap -a
ppid  pid   name        command           capabilities
0     1     root        sleep             chown, dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap
0     7     root        bash              chown, dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap
root@pod1:/#

root ユーザー + --privileged

cat << EOF > pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: pod2
  name: pod2
spec:
  containers:
  - args:
    - sleep
    - "3600"
    image: sotosugi/pscap-ubuntu
    name: pod2
    securityContext:
      privileged: true
EOF
k apply -f pod2.yaml

Pod の中から確認する。

$ k get po
NAME   READY   STATUS    RESTARTS   AGE
pod2   1/1     Running   0          7s
$ k exec -it pod2 -- /bin/bash
root@pod2:/# pscap -a
ppid  pid   name        command           capabilities
0     1     root        sleep             full
0     7     root        bash              full
root@pod2:/#

非 root ユーザー

cat << EOF > pod3.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: pod3
  name: pod3
spec:
  containers:
  - args:
    - sleep
    - "3600"
    image: sotosugi/pscap-ubuntu
    name: pod3
    securityContext:
      runAsUser: 65534
EOF
k apply -f pod3.yaml

Pod の中から確認する。

$ k get po
NAME   READY   STATUS    RESTARTS   AGE
pod3   1/1     Running   0          5s
$ k exec -it pod3 -- /bin/bash
nobody@pod3:/$ pscap -a
nobody@pod3:/$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
nobody       1     0  0 08:12 ?        00:00:00 sleep 3600
nobody       8     0  0 08:12 pts/0    00:00:00 /bin/bash
nobody      18     8  0 08:12 pts/0    00:00:00 ps -ef
nobody@pod3:/$

非 root ユーザー + --privileged

cat << EOF > pod4.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: pod4
  name: pod4
spec:
  containers:
  - args:
    - sleep
    - "3600"
    image: sotosugi/pscap-ubuntu
    name: pod4
    securityContext:
      privileged: true
      runAsUser: 65534
EOF
k apply -f pod4.yaml

Pod の中から確認する。

$ k get po
NAME   READY   STATUS    RESTARTS   AGE
pod4   1/1     Running   0          11s
$ k exec -it pod4 -- /bin/bash
nobody@pod4:/$ pscap -a
nobody@pod4:/$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
nobody       1     0  0 08:16 ?        00:00:00 sleep 3600
nobody       7     0  0 08:16 pts/0    00:00:00 /bin/bash
nobody      16     7  0 08:16 pts/0    00:00:00 ps -ef
nobody@pod4:/$

まとめ

docker run の場合と同じで、非 root ユーザーで実行している場合はそもそもケーパビリティーがなかった。なのでわざわざ Drop を書く必要はない。

補足

もしかして、pscap を実行するのにそもそも権限がいるのではないか?

念のためホストノード上から確認してみる。

一度 Pod を消して片方のノードを Cordon する。

$ k delete po --all
pod "pod1" deleted
pod "pod2" deleted
pod "pod3" deleted
pod "pod4" deleted
$ k get node
NAME                                                 STATUS   ROLES    AGE   VERSION
ip-192-168-175-232.ap-northeast-1.compute.internal   Ready    <none>   43d   v1.21.5-eks-bc4871b
ip-192-168-200-81.ap-northeast-1.compute.internal    Ready    <none>   43d   v1.21.5-eks-bc4871b
$ k cordon ip-192-168-175-232.ap-northeast-1.compute.internal
node/ip-192-168-175-232.ap-northeast-1.compute.internal cordoned
$ k get node
NAME                                                 STATUS                     ROLES    AGE   VERSION
ip-192-168-175-232.ap-northeast-1.compute.internal   Ready,SchedulingDisabled   <none>   43d   v1.21.5-eks-bc4871b
ip-192-168-200-81.ap-northeast-1.compute.internal    Ready                      <none>   43d   v1.21.5-eks-bc4871b

再び Pod を作成する。

$ k apply -f .
pod/pod1 created
pod/pod2 created
pod/pod3 created
pod/pod4 created
$ k get po -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP                NODE                                                NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          14s   192.168.255.108   ip-192-168-200-81.ap-northeast-1.compute.internal   <none>           <none>
pod2   1/1     Running   0          14s   192.168.211.63    ip-192-168-200-81.ap-northeast-1.compute.internal   <none>           <none>
pod3   1/1     Running   0          14s   192.168.196.0     ip-192-168-200-81.ap-northeast-1.compute.internal   <none>           <none>
pod4   1/1     Running   0          14s   192.168.242.153   ip-192-168-200-81.ap-northeast-1.compute.internal   <none>           <none>

SSM でノードに入り、ノードに pscap をインストールする。

yum -y install libcap-ng-utils

Docker コンテナとプロセスを確認する。

[root@ip-192-168-200-81 ~]# docker ps | grep sleep
a011874308c3   sotosugi/pscap-ubuntu                                                        "sleep 3600"             6 minutes ago   Up 6 minutes             k8s_pod3_pod3_default_522f8e93-5071-4f9d-8208-b41737fe05cc_0
a98b9cf6e0db   sotosugi/pscap-ubuntu                                                        "sleep 3600"             6 minutes ago   Up 6 minutes             k8s_pod4_pod4_default_e9c04a65-70f1-461d-b88e-b94a76c513a7_0
a529af7e125a   sotosugi/pscap-ubuntu                                                        "sleep 3600"             6 minutes ago   Up 6 minutes             k8s_pod1_pod1_default_b3c7857b-5d7f-4363-b860-e7732e18e538_0
0501ad27ec3f   sotosugi/pscap-ubuntu                                                        "sleep 3600"             6 minutes ago   Up 6 minutes             k8s_pod2_pod2_default_aee19f59-6020-4d7f-b494-dfffdb30e727_0
[root@ip-192-168-200-81 ~]# ps -ef | grep sleep
root     24277 24256  0 08:23 ?        00:00:00 sleep 3600
root     24351 24329  0 08:23 ?        00:00:00 sleep 3600
nfsnobo+ 24425 24405  0 08:23 ?        00:00:00 sleep 3600
nfsnobo+ 24497 24476  0 08:23 ?        00:00:00 sleep 3600
root     32404 26138  0 08:30 pts/0    00:00:00 grep --color=auto sleep
[root@ip-192-168-200-81 ~]#

このままでは対応がわからないので、inspect で調べる。

[root@ip-192-168-200-81 ~]# docker inspect a529af7e125a | grep Pid # これは pod1 なので root ユーザー
            "Pid": 24351,
            "PidMode": "",
            "PidsLimit": null,
[root@ip-192-168-200-81 ~]# docker inspect 0501ad27ec3f | grep Pid # これは pod2 なので root ユーザー + 特権
            "Pid": 24277,
            "PidMode": "",
            "PidsLimit": null,
[root@ip-192-168-200-81 ~]# docker inspect a011874308c3 | grep Pid # これは pod3 なので非 root ユーザー
            "Pid": 24497,
            "PidMode": "",
            "PidsLimit": null,
[root@ip-192-168-200-81 ~]# docker inspect a98b9cf6e0db | grep Pid # これは pod4 なので非 root ユーザー + 特権
            "Pid": 24425,
            "PidMode": "",
            "PidsLimit": null,
[root@ip-192-168-200-81 ~]#

ケーパビリティーを確認する。

[root@ip-192-168-200-81 ~]# pscap -a | grep -e 24351 -e 24277 -e 24497 -e 24425
24256 24277 root        sleep             full
24329 24351 root        sleep             chown, dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap
[root@ip-192-168-200-81 ~]#

やはり非 root ユーザーで実行したときはケーパビリティーはなかった。

読書メモ: 《増補改訂版2020》本好きのためのAmazon Kindle 読書術: 電子書籍の特性を活かして可処分時間を増やそう!

主に Kindle 本を Fire タブレットで読んでいるので、知らない Kindle の活用法がないかと思って読んだ本。

気になったところ

このような状況をさけるためには、本を購入する前にどの程度の分量の本なのかを確認しておく必要があります。

電子書籍だと本の厚みがわからないなと思っていた。文字の大きさでページ数も変わると思われる Kindle におけるページ数とは何だろうとは思うが、買う前にも本のページ数は確認できるし、端末上で「この本について」を開くことでページ数や、読書にかかる平均的な時間が確認できる。この本は 214 ページで平均的な読書時間は 1 時間 27 分。

2013年11月26日に電子書籍のあり方を大きく変え得る事件が起こりました。有名ブロガーであるChikirinさんがセルフパブリッシングで完全新作、書き下ろしのKindle本を出版したのです。

最近ちきりんさんの Voicy をよく聞いているが、ここでもちきりんさんがでてきた!

あまり知られていないのですが、KindleにはAmazonのサイト以外にもう一つKindle専用のページがあります(https://read.amazon.co.jp/notebook/)。こちらのサイトではAmazonのアカウントでログインができ、Kindleに関連した様々なサービスが提供されています。 この機能の一つにハイライト履歴の閲覧があります。Kindleでハイライトされた文章はこのKindle専用サイトのサーバにすべて保存されており、閲覧が可能です。

これは知らなかった。しかしアクセスしてみると、ハイライトの反映が遅そうだたったり、動作が遅そうだったり動きが怪しく感じたので、Kindle for Mac からハイライトを確認する方がよいかも?

エモーション・メモは『一冊からもっと学べるエモーショナル・リーディング』の著者・矢島雅弘さんが提唱している読書メモの取り方です。 エモーション・メモは、読書をしながら、気になった本の一節に感情をコメントしていきます。

これはよさそう。

私がおすすめする読書量を増やす簡単な方法は、「隙間時間を無駄にせず少しずつ本を読む」ことです。

「遅読家のための読書術」を読んだときには、短時間で読み切る方がよいと感じたので、この著者は反対のことをいっている。

私の読書記録の書き方は、本の中から気にいったセンテンスを引用し、コメントを残しておくというシンプルなものです。

私がブログに書評記事を書く際のフォーマットは、「ハイライトしてセンテンス3つ〜5つ+まとめ」という形をとっています。

これは「遅読家のための読書術」と同じ。

最近では、読書をするのが楽しいのか、読書記録を作るのが楽しいのかわからなくなることがあります。

Qiita 記事をたくさん書いていたとき同じように感じたので、よくわかる。

まとめ

Kindle の機能について、IT リテラシーがそれほど高くない人向けに丁寧に書いてあり、既に知っていることも多かったが、知らないこともちょいちょいあった。Evernoteブクログなど各種サービスやツールを活用ほうが説明されており、わりとアナログで手書きを推奨していた「遅読家のための読書術」とは反対。自分は Evernote も上手く使えなかったし、このようにブログのメモを残すのでよいかと思った。

読書にかかった時間は 2 時間半くらい。

そしてなんと、このメモを書いていたら、改訂増補版 2022 が出ていたことに気がついた。

Consul を試す

Consul を試すメモ。

コンポーネント バージョン 備考
eksctl 0.80.0
Kubernetes バージョン 1.21
プラットフォームのバージョン eks.4
Consul CLI v1.11.2
Consul チャート 0.35.0

クラスターの作成

1.21 のクラスターをノードなしで作成する。

CLUSTER_NAME="consul"
cat << EOF > cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: ${CLUSTER_NAME}
  region: ap-northeast-1
  version: "1.21"
vpc:
  cidr: "10.0.0.0/16"

availabilityZones:
  - ap-northeast-1a
  - ap-northeast-1c

cloudWatch:
  clusterLogging:
    enableTypes: ["*"]

iam:
  withOIDC: true
EOF
eksctl create cluster -f cluster.yaml

ノードを作成する。

cat << EOF > managed-ng-1.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: ${CLUSTER_NAME}
  region: ap-northeast-1

managedNodeGroups:
  - name: managed-ng-1
    minSize: 2
    maxSize: 10
    desiredCapacity: 2
    privateNetworking: true
    iam:
      attachPolicyARNs:
        - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
EOF
eksctl create nodegroup -f managed-ng-1.yaml

Admin ロールにも権限をつけておく。

USER_NAME="Admin:{{SessionName}}"
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/Admin"
eksctl create iamidentitymapping --cluster ${CLUSTER_NAME} --arn ${ROLE_ARN} --username ${USER_NAME} --group system:masters

Consul のデプロイ

Consul CLI をインストールする。

brew tap hashicorp/tap
brew install hashicorp/tap/consul

確認する。

$ consul version
Consul v1.11.2
Revision 37c7d06b
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)

チュートリアルリポジトリをクローンする。

git clone https://github.com/hashicorp/learn-consul-kubernetes.git
cd learn-consul-kubernetes/service-mesh/deploy

チャートリポジトリを追加する。

helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update

チャートを確認する。

$ helm search repo hashicorp/consul
NAME                    CHART VERSION   APP VERSION     DESCRIPTION
hashicorp/consul        0.39.0          1.11.1          Official HashiCorp Consul Chart

valuse ファイルを確認する。

global:
  name: consul
  datacenter: dc1
  image: hashicorp/consul:1.10.3
  imageEnvoy: envoyproxy/envoy:v1.18.4
  imageK8S: hashicorp/consul-k8s-control-plane:0.34.1
  metrics:
    enabled: true
    enableAgentMetrics: true
server:
  replicas: 1
ui:
  enabled: true
connectInject:
  enabled: true
  default: true
controller:
  enabled: true
prometheus:
  enabled: true
grafana:
  enabled: true

Consul をデプロイする。

$ helm install -f config.yaml consul hashicorp/consul --create-namespace -n consul --version "0.35.0"
NAME: consul
LAST DEPLOYED: Tue Jan 25 08:21:47 2022
NAMESPACE: consul
STATUS: deployed
REVISION: 1
NOTES:
Thank you for installing HashiCorp Consul!

Now that you have deployed Consul, you should look over the docs on using
Consul with Kubernetes available here:

https://www.consul.io/docs/platform/k8s/index.html


Your release is named consul.

To learn more about the release, run:

  $ helm status consul
  $ helm get all consul

Pod を確認する。

$ k -n consul get po
NAME                                                          READY   STATUS    RESTARTS   AGE
consul-265gh                                                  1/1     Running   0          66s
consul-6x524                                                  1/1     Running   0          66s
consul-connect-injector-webhook-deployment-7586fcf6f8-9vrg4   1/1     Running   0          66s
consul-connect-injector-webhook-deployment-7586fcf6f8-kh79n   1/1     Running   0          66s
consul-controller-5b98b9b584-6p2wv                            1/1     Running   0          66s
consul-server-0                                               1/1     Running   0          66s
consul-webhook-cert-manager-c59995c7c-2pv2c                   1/1     Running   0          66s
prometheus-server-5cbddcc44b-zgtph                            2/2     Running   0          25s

Consul Server は StatefulSet で、PV を使っている。各ノードに DeamonSet のノードもいる。

$ k get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                STORAGECLASS   REASON   AGE
pvc-6114c28a-c49e-4626-a542-a48ac6433a01   10Gi       RWO            Delete           Bound    consul/data-consul-consul-server-0   gp2                     74s
$ k -n consul get pvc
NAME                          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data-consul-consul-server-0   Bound    pvc-6114c28a-c49e-4626-a542-a48ac6433a01   10Gi       RWO            gp2            89s

UI にアクセスするためにポートフォワードする。http://localhost:18500

kubectl --namespace consul port-forward service/consul-ui 18500:80 --address 0.0.0.0

http://localhost:18500 にアクセスする。

f:id:sotoiwa:20220125165203p:plain

CLI は Consul Server 上で実行できる。

$ kubectl -n consul exec -it consul-server-0 -- consul members
Node                                             Address            Status  Type    Build   Protocol  DC   Segment
consul-server-0                                  10.0.114.112:8301  alive   server  1.10.3  2         dc1  <all>
ip-10-0-103-225.ap-northeast-1.compute.internal  10.0.103.144:8301  alive   client  1.10.3  2         dc1  <default>
ip-10-0-94-2.ap-northeast-1.compute.internal     10.0.93.160:8301   alive   client  1.10.3  2         dc1  <default>

Consul Server の 8500 へのポートフォワードをすれば、ローカルマシン上の CLI も実行できる。

kubectl --namespace consul port-forward service/consul-server 8500:8500
$ consul members
Node                                             Address            Status  Type    Build   Protocol  DC   Partition  Segment
consul-server-0                                  10.0.114.112:8301  alive   server  1.10.3  2         dc1  default    <all>
ip-10-0-103-225.ap-northeast-1.compute.internal  10.0.103.144:8301  alive   client  1.10.3  2         dc1  default    <default>
ip-10-0-94-2.ap-northeast-1.compute.internal     10.0.93.160:8301   alive   client  1.10.3  2         dc1  default    <default>

デモアプリのデプロイ

デモアプリをデプロイする。

$ kubectl apply -f hashicups/
service/frontend created
serviceaccount/frontend created
servicedefaults.consul.hashicorp.com/frontend created
configmap/nginx-configmap created
deployment.apps/frontend created
service/postgres created
serviceaccount/postgres created
servicedefaults.consul.hashicorp.com/postgres created
deployment.apps/postgres created
service/product-api created
serviceaccount/product-api created
servicedefaults.consul.hashicorp.com/product-api created
configmap/db-configmap created
deployment.apps/product-api created
service/public-api created
serviceaccount/public-api created
servicedefaults.consul.hashicorp.com/public-api created
deployment.apps/public-api created

Pod を確認する。

$ k get po -L consul.hashicorp.com/connect-inject-status
NAME                           READY   STATUS    RESTARTS   AGE   CONNECT-INJECT-STATUS
frontend-98cb6859b-v7b7g       2/2     Running   0          69s   injected
postgres-6ccb6d9968-7zc8m      2/2     Running   0          69s   injected
product-api-6798bc4b4d-nvsdn   2/2     Running   0          69s   injected
public-api-5bdf986897-nmcbw    2/2     Running   0          69s   injected

デモアプリを探検

アプリにアクセスするためポートフォワードする。

kubectl port-forward service/frontend 18080:80 --address 0.0.0.0

http://localhost:18080/ でアプリにアクセスする。

f:id:sotoiwa:20220125165222p:plain

ダッシュボードではサービスが確認できる。

f:id:sotoiwa:20220125165237p:plain

サービスを選択すると、トポロジーが確認できる。

f:id:sotoiwa:20220125165251p:plain

Consul の CRD を見てみる。

$ k get crd
NAME                                         CREATED AT
eniconfigs.crd.k8s.amazonaws.com             2022-01-24T22:18:02Z
ingressgateways.consul.hashicorp.com         2022-01-24T23:21:52Z
meshes.consul.hashicorp.com                  2022-01-24T23:21:52Z
proxydefaults.consul.hashicorp.com           2022-01-24T23:21:52Z
securitygrouppolicies.vpcresources.k8s.aws   2022-01-24T22:18:05Z
servicedefaults.consul.hashicorp.com         2022-01-24T23:21:52Z
serviceintentions.consul.hashicorp.com       2022-01-24T23:21:52Z
serviceresolvers.consul.hashicorp.com        2022-01-24T23:21:52Z
servicerouters.consul.hashicorp.com          2022-01-24T23:21:52Z
servicesplitters.consul.hashicorp.com        2022-01-24T23:21:52Z
terminatinggateways.consul.hashicorp.com     2022-01-24T23:21:52Z

Linkerd を試す

Linkerd を試すメモ。

コンポーネント バージョン 備考
eksctl 0.80.0
Kubernetes バージョン 1.21
プラットフォームのバージョン eks.4
Linkerd CLI 2.11.1

クラスターの作成

1.21 のクラスターをノードなしで作成する。

CLUSTER_NAME="linkerd"
cat << EOF > cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: ${CLUSTER_NAME}
  region: ap-northeast-1
  version: "1.21"
vpc:
  cidr: "10.0.0.0/16"

availabilityZones:
  - ap-northeast-1a
  - ap-northeast-1c

cloudWatch:
  clusterLogging:
    enableTypes: ["*"]

iam:
  withOIDC: true
EOF
eksctl create cluster -f cluster.yaml

ノードを作成する。

cat << EOF > managed-ng-1.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: ${CLUSTER_NAME}
  region: ap-northeast-1

managedNodeGroups:
  - name: managed-ng-1
    minSize: 2
    maxSize: 10
    desiredCapacity: 2
    privateNetworking: true
    iam:
      attachPolicyARNs:
        - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
EOF
eksctl create nodegroup -f managed-ng-1.yaml

Admin ロールにも権限をつけておく。

USER_NAME="Admin:{{SessionName}}"
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/Admin"
eksctl create iamidentitymapping --cluster ${CLUSTER_NAME} --arn ${ROLE_ARN} --username ${USER_NAME} --group system:masters

Linkerd CLI のインストール

Linkerd CLI をインストールする。

brew install linkerd

バージョンを確認する。

$ linkerd version
Client version: stable-2.11.1
Server version: unavailable

クラスターのチェック

クラスターの前提条件をチェックする。

$ linkerd check --pre
Linkerd core checks
===================

kubernetes-api
--------------
√ can initialize the client
√ can query the Kubernetes API

kubernetes-version
------------------
√ is running the minimum Kubernetes API version
√ is running the minimum kubectl version

pre-kubernetes-setup
--------------------
√ control plane namespace does not already exist
√ can create non-namespaced resources
√ can create ServiceAccounts
√ can create Services
√ can create Deployments
√ can create CronJobs
√ can create ConfigMaps
√ can create Secrets
√ can read Secrets
√ can read extension-apiserver-authentication configmap
√ no clock skew detected

linkerd-version
---------------
√ can determine the latest version
√ cli is up-to-date

Status check results are √

コントロールプレーンのデプロイ

コントロールプレーンをデプロイする。Helm チャートもある。

$ linkerd install | kubectl apply -f -
namespace/linkerd created
clusterrole.rbac.authorization.k8s.io/linkerd-linkerd-identity created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-linkerd-identity created
serviceaccount/linkerd-identity created
clusterrole.rbac.authorization.k8s.io/linkerd-linkerd-destination created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-linkerd-destination created
serviceaccount/linkerd-destination created
secret/linkerd-sp-validator-k8s-tls created
validatingwebhookconfiguration.admissionregistration.k8s.io/linkerd-sp-validator-webhook-config created
secret/linkerd-policy-validator-k8s-tls created
validatingwebhookconfiguration.admissionregistration.k8s.io/linkerd-policy-validator-webhook-config created
clusterrole.rbac.authorization.k8s.io/linkerd-policy created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-destination-policy created
role.rbac.authorization.k8s.io/linkerd-heartbeat created
rolebinding.rbac.authorization.k8s.io/linkerd-heartbeat created
clusterrole.rbac.authorization.k8s.io/linkerd-heartbeat created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-heartbeat created
serviceaccount/linkerd-heartbeat created
customresourcedefinition.apiextensions.k8s.io/servers.policy.linkerd.io created
customresourcedefinition.apiextensions.k8s.io/serverauthorizations.policy.linkerd.io created
customresourcedefinition.apiextensions.k8s.io/serviceprofiles.linkerd.io created
customresourcedefinition.apiextensions.k8s.io/trafficsplits.split.smi-spec.io created
clusterrole.rbac.authorization.k8s.io/linkerd-linkerd-proxy-injector created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-linkerd-proxy-injector created
serviceaccount/linkerd-proxy-injector created
secret/linkerd-proxy-injector-k8s-tls created
mutatingwebhookconfiguration.admissionregistration.k8s.io/linkerd-proxy-injector-webhook-config created
configmap/linkerd-config created
secret/linkerd-identity-issuer created
configmap/linkerd-identity-trust-roots created
service/linkerd-identity created
service/linkerd-identity-headless created
deployment.apps/linkerd-identity created
service/linkerd-dst created
service/linkerd-dst-headless created
service/linkerd-sp-validator created
service/linkerd-policy created
service/linkerd-policy-validator created
deployment.apps/linkerd-destination created
Warning: batch/v1beta1 CronJob is deprecated in v1.21+, unavailable in v1.25+; use batch/v1 CronJob
cronjob.batch/linkerd-heartbeat created
deployment.apps/linkerd-proxy-injector created
service/linkerd-proxy-injector created
secret/linkerd-config-overrides created

デプロイをチェックする。

$ linkerd check
Linkerd core checks
===================

kubernetes-api
--------------
√ can initialize the client
√ can query the Kubernetes API

kubernetes-version
------------------
√ is running the minimum Kubernetes API version
√ is running the minimum kubectl version

linkerd-existence
-----------------
√ 'linkerd-config' config map exists
√ heartbeat ServiceAccount exist
√ control plane replica sets are ready
√ no unschedulable pods
√ control plane pods are ready

linkerd-config
--------------
√ control plane Namespace exists
√ control plane ClusterRoles exist
√ control plane ClusterRoleBindings exist
√ control plane ServiceAccounts exist
√ control plane CustomResourceDefinitions exist
√ control plane MutatingWebhookConfigurations exist
√ control plane ValidatingWebhookConfigurations exist

linkerd-identity
----------------
√ certificate config is valid
√ trust anchors are using supported crypto algorithm
√ trust anchors are within their validity period
√ trust anchors are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
√ issuer cert is valid for at least 60 days
√ issuer cert is issued by the trust anchor

linkerd-webhooks-and-apisvc-tls
-------------------------------
√ proxy-injector webhook has valid cert
√ proxy-injector cert is valid for at least 60 days
√ sp-validator webhook has valid cert
√ sp-validator cert is valid for at least 60 days
√ policy-validator webhook has valid cert
√ policy-validator cert is valid for at least 60 days

linkerd-version
---------------
√ can determine the latest version
√ cli is up-to-date

control-plane-version
---------------------
√ can retrieve the control plane version
√ control plane is up-to-date
√ control plane and cli versions match

linkerd-control-plane-proxy
---------------------------
√ control plane proxies are healthy
√ control plane proxies are up-to-date
√ control plane proxies and cli versions match

Status check results are √

Pod を確認する。

$ k get po -A
NAMESPACE     NAME                                      READY   STATUS    RESTARTS   AGE
kube-system   aws-node-ltd8k                            1/1     Running   0          7h6m
kube-system   aws-node-xdblx                            1/1     Running   0          7h6m
kube-system   coredns-76f4967988-p5njc                  1/1     Running   0          7h34m
kube-system   coredns-76f4967988-xgjss                  1/1     Running   0          7h34m
kube-system   kube-proxy-dmc2v                          1/1     Running   0          7h6m
kube-system   kube-proxy-s5vvz                          1/1     Running   0          7h6m
linkerd       linkerd-destination-685b865bdb-t54s9      4/4     Running   0          117s
linkerd       linkerd-identity-6b78ff444f-h28v7         2/2     Running   0          117s
linkerd       linkerd-proxy-injector-67475764f4-wgpm6   2/2     Running   0          117s

デモアプリのインストール

デモアプリをインストールする。

$ curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/emojivoto.yml \
  | kubectl apply -f -
namespace/emojivoto created
serviceaccount/emoji created
serviceaccount/voting created
serviceaccount/web created
service/emoji-svc created
service/voting-svc created
service/web-svc created
deployment.apps/emoji created
deployment.apps/vote-bot created
deployment.apps/voting created
deployment.apps/web created

確認する。

$ k -n emojivoto get po,svc
NAME                            READY   STATUS    RESTARTS   AGE
pod/emoji-66ccdb4d86-m7rpm      1/1     Running   0          3m25s
pod/vote-bot-69754c864f-vp4x7   1/1     Running   0          3m25s
pod/voting-f999bd4d7-p7cgr      1/1     Running   0          3m25s
pod/web-79469b946f-bnwwv        1/1     Running   0          3m25s

NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
service/emoji-svc    ClusterIP   172.20.117.164   <none>        8080/TCP,8801/TCP   3m26s
service/voting-svc   ClusterIP   172.20.27.217    <none>        8080/TCP,8801/TCP   3m26s
service/web-svc      ClusterIP   172.20.238.239   <none>        80/TCP              3m26s

外に公開されていないので、ポートフォワードしてから、http://localhost:8080 にアクセスする。

kubectl -n emojivoto port-forward svc/web-svc 8080:80

f:id:sotoiwa:20220125163910p:plain

サイドカーのインジェクション

デモアプリに、サイドカーをインジェクトする。

$ kubectl get -n emojivoto deploy -o yaml \
  | linkerd inject - \
  | kubectl apply -f -

deployment "emoji" injected
deployment "vote-bot" injected
deployment "voting" injected
deployment "web" injected

deployment.apps/emoji configured
deployment.apps/vote-bot configured
deployment.apps/voting configured
deployment.apps/web configured

このコマンドは Deployment の PodTemplate にアノテーションを追加しているだけ。サイドカーをインジェクトするのは Pod のデプロイのタイミングの模様。

$ k -n emojivoto get deploy web -o yaml
apiVersion: apps/v1
kind: Deployment
spec:
...
  template:
    metadata:
      annotations:
        linkerd.io/inject: enabled
...

Pod は 2/2 になっておりサイドカーが入っている。

$ k -n emojivoto get po
NAME                        READY   STATUS    RESTARTS   AGE
emoji-696d9d8f95-mcgff      2/2     Running   0          9m47s
vote-bot-6d7677bb68-tlbf4   2/2     Running   0          9m47s
voting-ff4c54b8d-wb9kk      2/2     Running   0          9m47s
web-5f86686c4d-s5kf8        2/2     Running   0          9m47s

プロキシがインジェクトされていることをチェックする。

$ linkerd -n emojivoto check --proxy
Linkerd core checks
===================

kubernetes-api
--------------
√ can initialize the client
√ can query the Kubernetes API

kubernetes-version
------------------
√ is running the minimum Kubernetes API version
√ is running the minimum kubectl version

linkerd-existence
-----------------
√ 'linkerd-config' config map exists
√ heartbeat ServiceAccount exist
√ control plane replica sets are ready
√ no unschedulable pods
√ control plane pods are ready

linkerd-config
--------------
√ control plane Namespace exists
√ control plane ClusterRoles exist
√ control plane ClusterRoleBindings exist
√ control plane ServiceAccounts exist
√ control plane CustomResourceDefinitions exist
√ control plane MutatingWebhookConfigurations exist
√ control plane ValidatingWebhookConfigurations exist

linkerd-identity
----------------
√ certificate config is valid
√ trust anchors are using supported crypto algorithm
√ trust anchors are within their validity period
√ trust anchors are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
√ issuer cert is valid for at least 60 days
√ issuer cert is issued by the trust anchor

linkerd-webhooks-and-apisvc-tls
-------------------------------
√ proxy-injector webhook has valid cert
√ proxy-injector cert is valid for at least 60 days
√ sp-validator webhook has valid cert
√ sp-validator cert is valid for at least 60 days
√ policy-validator webhook has valid cert
√ policy-validator cert is valid for at least 60 days

linkerd-identity-data-plane
---------------------------
√ data plane proxies certificate match CA

linkerd-version
---------------
√ can determine the latest version
√ cli is up-to-date

linkerd-control-plane-proxy
---------------------------
√ control plane proxies are healthy
√ control plane proxies are up-to-date
√ control plane proxies and cli versions match

linkerd-data-plane
------------------
√ data plane namespace exists
√ data plane proxies are ready
√ data plane is up-to-date
√ data plane and cli versions match
√ data plane pod labels are configured correctly
√ data plane service labels are configured correctly
√ data plane service annotations are configured correctly
√ opaque ports are properly annotated

Status check results are √

Linkerd を探索する

コントロールプレーンのコア機能は機能が最小限なので、エクステンションを追加する。

クラスター上にメトリクススタックをデプロイする viz エクステンションを追加する。

$ linkerd viz install | kubectl apply -f -
namespace/linkerd-viz created
clusterrole.rbac.authorization.k8s.io/linkerd-linkerd-viz-metrics-api created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-linkerd-viz-metrics-api created
serviceaccount/metrics-api created
serviceaccount/grafana created
clusterrole.rbac.authorization.k8s.io/linkerd-linkerd-viz-prometheus created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-linkerd-viz-prometheus created
serviceaccount/prometheus created
clusterrole.rbac.authorization.k8s.io/linkerd-linkerd-viz-tap created
clusterrole.rbac.authorization.k8s.io/linkerd-linkerd-viz-tap-admin created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-linkerd-viz-tap created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-linkerd-viz-tap-auth-delegator created
serviceaccount/tap created
rolebinding.rbac.authorization.k8s.io/linkerd-linkerd-viz-tap-auth-reader created
secret/tap-k8s-tls created
apiservice.apiregistration.k8s.io/v1alpha1.tap.linkerd.io created
role.rbac.authorization.k8s.io/web created
rolebinding.rbac.authorization.k8s.io/web created
clusterrole.rbac.authorization.k8s.io/linkerd-linkerd-viz-web-check created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-linkerd-viz-web-check created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-linkerd-viz-web-admin created
clusterrole.rbac.authorization.k8s.io/linkerd-linkerd-viz-web-api created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-linkerd-viz-web-api created
serviceaccount/web created
server.policy.linkerd.io/admin created
serverauthorization.policy.linkerd.io/admin created
server.policy.linkerd.io/proxy-admin created
serverauthorization.policy.linkerd.io/proxy-admin created
service/metrics-api created
deployment.apps/metrics-api created
server.policy.linkerd.io/metrics-api created
serverauthorization.policy.linkerd.io/metrics-api created
configmap/grafana-config created
service/grafana created
deployment.apps/grafana created
server.policy.linkerd.io/grafana created
serverauthorization.policy.linkerd.io/grafana created
configmap/prometheus-config created
service/prometheus created
deployment.apps/prometheus created
service/tap created
deployment.apps/tap created
server.policy.linkerd.io/tap-api created
serverauthorization.policy.linkerd.io/tap created
clusterrole.rbac.authorization.k8s.io/linkerd-tap-injector created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-tap-injector created
serviceaccount/tap-injector created
secret/tap-injector-k8s-tls created
mutatingwebhookconfiguration.admissionregistration.k8s.io/linkerd-tap-injector-webhook-config created
service/tap-injector created
deployment.apps/tap-injector created
server.policy.linkerd.io/tap-injector-webhook created
serverauthorization.policy.linkerd.io/tap-injector created
service/web created
deployment.apps/web created
serviceprofile.linkerd.io/metrics-api.linkerd-viz.svc.cluster.local created
serviceprofile.linkerd.io/prometheus.linkerd-viz.svc.cluster.local created
serviceprofile.linkerd.io/grafana.linkerd-viz.svc.cluster.local created

エクステンションをチェックする。

$ linkerd check
Linkerd core checks
===================

kubernetes-api
--------------
√ can initialize the client
√ can query the Kubernetes API

kubernetes-version
------------------
√ is running the minimum Kubernetes API version
√ is running the minimum kubectl version

linkerd-existence
-----------------
√ 'linkerd-config' config map exists
√ heartbeat ServiceAccount exist
√ control plane replica sets are ready
√ no unschedulable pods
√ control plane pods are ready

linkerd-config
--------------
√ control plane Namespace exists
√ control plane ClusterRoles exist
√ control plane ClusterRoleBindings exist
√ control plane ServiceAccounts exist
√ control plane CustomResourceDefinitions exist
√ control plane MutatingWebhookConfigurations exist
√ control plane ValidatingWebhookConfigurations exist

linkerd-identity
----------------
√ certificate config is valid
√ trust anchors are using supported crypto algorithm
√ trust anchors are within their validity period
√ trust anchors are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
√ issuer cert is valid for at least 60 days
√ issuer cert is issued by the trust anchor

linkerd-webhooks-and-apisvc-tls
-------------------------------
√ proxy-injector webhook has valid cert
√ proxy-injector cert is valid for at least 60 days
√ sp-validator webhook has valid cert
√ sp-validator cert is valid for at least 60 days
√ policy-validator webhook has valid cert
√ policy-validator cert is valid for at least 60 days

linkerd-version
---------------
√ can determine the latest version
√ cli is up-to-date

control-plane-version
---------------------
√ can retrieve the control plane version
√ control plane is up-to-date
√ control plane and cli versions match

linkerd-control-plane-proxy
---------------------------
√ control plane proxies are healthy
√ control plane proxies are up-to-date
√ control plane proxies and cli versions match

Status check results are √

Linkerd extensions checks
=========================

linkerd-viz
-----------
√ linkerd-viz Namespace exists
√ linkerd-viz ClusterRoles exist
√ linkerd-viz ClusterRoleBindings exist
√ tap API server has valid cert
√ tap API server cert is valid for at least 60 days
√ tap API service is running
√ linkerd-viz pods are injected
√ viz extension pods are running
√ viz extension proxies are healthy
√ viz extension proxies are up-to-date
√ viz extension proxies and cli versions match
√ prometheus is installed and configured correctly
√ can initialize the client
√ viz extension self-check

Status check results are √

Pod を確認する。

$ k -n linkerd-viz get po
NAME                            READY   STATUS    RESTARTS   AGE
grafana-8d54d5f6d-25wn6         2/2     Running   0          61s
metrics-api-6c59967bf4-zwpxl    2/2     Running   0          61s
prometheus-7bbc4d8c5b-r646x     2/2     Running   0          61s
tap-86df5fc5b8-94wfv            2/2     Running   0          60s
tap-injector-544f99cc4f-s9v52   2/2     Running   0          60s
web-db97ff489-c95qb             2/2     Running   0          60s

ダッシュボードを開く。

$ linkerd viz dashboard
Linkerd dashboard available at:
http://localhost:50750
Grafana dashboard available at:
http://localhost:50750/grafana
Opening Linkerd dashboard in the default browser

f:id:sotoiwa:20220125163934p:plain

Linkerd の CRD を見てみる。

$ k get crd
NAME                                         CREATED AT
eniconfigs.crd.k8s.amazonaws.com             2022-01-24T00:51:55Z
securitygrouppolicies.vpcresources.k8s.aws   2022-01-24T00:51:58Z
serverauthorizations.policy.linkerd.io       2022-01-24T08:24:54Z
servers.policy.linkerd.io                    2022-01-24T08:24:54Z
serviceprofiles.linkerd.io                   2022-01-24T08:24:54Z
trafficsplits.split.smi-spec.io              2022-01-24T08:24:54Z