tk_ch’s blog

インフラエンジニアのブログ

GCP 検証用GKEクラスタを作成する(K8s v1.25)

Kubernetes(K8s)の検証をしたいけど自分でK8sクラスタを立てるのは面倒、、、というときは、パブリッククラウドKubernetesサービスを使うと便利。
中でもGoogle Cloud Platform(GCP)のKubernetesサービスであるGoogle Kubernetes Engine(GKE)はクラスタ作成にかかる時間が短く、無料枠も充実しているのでよく使っている。
検証用のGKEクラスタを準備した際のメモを残しておく。

※無料枠について(2022年10月時点)

  • GCPは、アカウントごとに1回だけ、無料プログラムとして90 日間有効な300ドル分のクレジットを提供してくれる。
  • GKEは、クラスタが存在するだけで1時間1クラスタあたり0.10ドルのクラスタ管理手数料がかかる。
    しかし、GKEの無料枠として1クラスタ分のクラスタ管理手数料相当のクレジット(月額74.40ドル)が毎月提供されるので、1クラスタまではクラスタ管理手数料が実質無料になっている(上記の無料プログラムとは別)。
    標準クラスタを使用する場合は、クラスタ管理手数料以外にワーカーノードのGoogle Compute Engine(GCE)のコストがかかるが、無料プログラムと併用したり、使わないときはインスタンスを停止するなどすればかなり節約できる。

環境

  • GKE:v1.25.1-gke.500

実施内容

作成するGKEクラスタの運用モードを検討

GKEクラスタのタイプの説明を読んで、作成するGKEクラスタの運用モードを検討する。

運用モードには以下の2種類がある。

  • Autopilot: 完全にプロビジョニングされたマネージド クラスタ構成を提供します。Autopilot モードで作成されたクラスタには、クラスタ構成オプションが自動的に作成されます。Autopilot クラスタは、本番環境ワークロードに対応する最適化されたクラスタ構成を使用してあらかじめ構成されています。

  • Standard: クラスタの基盤となるインフラストラクチャに高度な構成の柔軟性を提供します。Standard モードを使用して作成されたクラスタでは、ユーザーが本番環境ワークロードに必要な構成を決定します。

→AutoPilotの方が楽そうだが、GCPに管理を任せる範囲が広い分、Autopilotならではの制約もある。
検証・勉強用途なので、制約の少ないStandardモードにする。

GKEクラスタを作成する

GCPの以下ドキュメントを参考に、GKEクラスタを作成してみる。
cloud.google.com

GCPのナビゲーションメニュー(画面左上)から、「Kubernetes Engine」をクリックする。
GKEを初めて使う場合は、以下のようにKubernetes Engine APIの有効化画面が表示されるので、「有効にする」をクリックする。

Kubernetes Engine APIの有効化
Kubernetes Engine APIの有効化

しばらく待つと以下の画面になるので「作成」をクリックする。

GKEのページ
GKEのページ

次は運用モードの選択。Standardの「構成」をクリックする。

運用モードの選択
運用モードの選択

クラスター名とロケーションタイプを入力する。
ロケーションタイプはデフォルトの「ゾーン」にした。
使用するゾーンはどこでもよいが、自分はGCEの無料枠オレゴンリージョン(us-west1)に作成したVMインスタンスからGKEに接続する予定のため、オレゴンリージョンのゾーンにした。

クラスタの作成1
クラスタの作成1

コントロールプレーンのバージョンを選択する。
今回は出来るだけ新しいバージョンのK8sを使いたかったので、Rapidチャンネルの最新バージョンを選択した。

クラスタの作成2
クラスタの作成2

最後に画面下部の「作成」をクリックする。
※他にもワーカーノードのインスタンスタイプやクラスタのネットワークなど、色々設定ができるが、そこは特に触らずデフォルトのまま作成した。
しばらく待つと、以下のようにGKEクラスタが作成される。

作成されたクラスタ
作成されたクラスタ

クラスタを選択して「ノード」タブを見ると、e2-mediumのGCEインスタンス3台がワーカーノードとしてクラスタに参加していることが分かる。

ワーカーノードの詳細
ワーカーノードの詳細

Cloud ShellからGKEクラスタに接続する

クラスタができたので、kubectlで操作してみる。
とりあえず、こちらのページを参考に、Cloud Shellでクラスタに接続する。

GCPの管理コンソール右上の、「Cloud Shell をアクティブにする」をクリックしてCloud Shellを起動し、以下を実行する。

デフォルトのプロジェクトを設定
$ gcloud config set project {GKEクラスタを作成したプロジェクトのID}

クラスタ名とクラスタが存在するゾーンを指定して、クラスタ認証情報を取得
$ gcloud container clusters get-credentials cluster-1 --region us-west1-a

kubectlが実行できるようになったことを確認
$ kubectl get node
NAME                                       STATUS   ROLES    AGE   VERSION
gke-cluster-1-default-pool-71e862dd-7b78   Ready    <none>   69m   v1.25.1-gke.500
gke-cluster-1-default-pool-71e862dd-lbk8   Ready    <none>   69m   v1.25.1-gke.500
gke-cluster-1-default-pool-71e862dd-rh0q   Ready    <none>   69m   v1.25.1-gke.500

GKEクラスタの動作確認をする

kubectlを実行できるようになったので、こちらを参考にサンプルAPを使った動作確認をする。
そのままの手順だとServiceがインターネットに公開されてしまうので、Serviceのyaml内の loadBalancerSourceRangesに自分の端末が使っているグローバルIPを指定してソースIP制限している。

サンプルDeploymentのyamlを作成
$ cat <<EOF > hello-server-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-server
  labels:
    app: hello
spec:
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - name: hello-app
        image: us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0
        ports:
        - containerPort: 8080
EOF

サンプルDeploymentを作成
$ kubectl apply -f hello-server-deployment.yaml
deployment.apps/hello-server created

正常に作成されたことを確認
$ kubectl get deployment
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
hello-server   1/1     1            1           27s
$ kubectl get pod
NAME                            READY   STATUS    RESTARTS   AGE
hello-server-6cfcb8c7df-8bb2z   1/1     Running   0          31s

サンプルServiceのyamlを作成
$ cat <<EOF > hello-server-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hello-server
  labels:
    app: hello
spec:
  selector:
    app: hello
  ports:
  - port: 80
    targetPort: 8080
  type: LoadBalancer
  loadBalancerSourceRanges:
  - {自分の端末のグローバルIPを記載}/32
EOF

サンプルServiceを作成
$ kubectl apply -f hello-server-service.yaml
service/hello-server created

EXTERNAL-IPがPENDINGからIPの表示に変わるのを待つ
$ kubectl get service hello-server -w
NAME           TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
hello-server   LoadBalancer   10.96.4.233   <pending>     80:30722/TCP   17s
hello-server   LoadBalancer   10.96.4.233   34.105.117.184   80:30722/TCP   36s

ブラウザで「http://EXTERNAL_IP」にアクセスする。
以下のように表示されれば、正常にサンプルアプリを外部公開できている。

サンプルAPの画面
サンプルAPの画面

確認できたら、動作確認用に作成したリソースを削除する。

$ kubectl delete -f hello-server-service.yaml
service "hello-server" deleted

$ kubectl delete -f hello-server-deployment.yaml
deployment.apps "hello-server" deleted

GCEのVMインスタンス(CentOS7)からGKEクラスタに接続する

諸事情で、Cloud ShellじゃなくGCEのVMインスタンスからkubectlでクラスタを操作したいので、接続を試してみる。 VMインスタンスは以下記事で作成したものを使用する。
tk-ch.hatenablog.com

kubectl をインストールしてクラスタ アクセスを構成する」を参考に設定を進める。

kubectlをインストールする

gcloudコマンドでインストール
$ gcloud components install kubectl

kubectlのバージョンを確認
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"22+", GitVersion:"v1.22.14-dispatcher-dirty", GitCommit:"009f3ac5f1d09b1ec5d4dc811b6032271fe61f75", GitTreeState:"dirty", BuildDate:"2022-09-19T01:31:36Z", GoVersion:"go1.16.15", Compiler:"gc", Platform:"linux/amd64"}
The connection to the server localhost:8080 was refused - did you specify the right host or port?

こちらに以下の記載がある。

Kubernetes バージョン 1.26 がリリースされる前に gcloud CLI を起動する場合は、gke-gcloud-auth-plugin バイナリのインストールが必要になります。インストールされていない場合は、kubectl などのカスタム Kubernetes クライアントの既存のインストールは機能しなくなります。 kubectl などのクライアントで GKE を操作するには、このプラグインをインストールする必要があります。

作業時点で1.26はリリースされていないので、gke-gcloud-auth-pluginが必要。
確認したところ、kubectlと一緒にインストールされていた。

$ gke-gcloud-auth-plugin --version
Kubernetes v1.25.2-alpha+ae91c1fc0c443c464a4c878ffa2a4544483c6d1f

kubectlにGKEクラスタの情報を設定する。

クラスタ名とクラスタが存在するゾーンを指定して、クラスタ認証情報を取得する
$ gcloud container clusters get-credentials cluster-1 --region us-west1-a
Fetching cluster endpoint and auth data.
kubeconfig entry generated for cluster-1.

※「gcloud container clusters get-credentials」実行時に、以下のエラーになった場合

クラスタ名とクラスタが存在するゾーンを指定して、クラスタ認証情報を取得する
$ gcloud container clusters get-credentials cluster-1 --region us-west1-a
Fetching cluster endpoint and auth data.
ERROR: (gcloud.container.clusters.get-credentials) ResponseError: code=403, message=Request had insufficient authentication scopes.

If you are in a compute engine VM, it is likely that the specified scopes during VM creation are not enough to run this command.
See https://cloud.google.com/compute/docs/access/service-accounts#accesscopesiam for more information about access scopes.
See https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances#changeserviceaccountandscopes for how to update access scopes of the VM.

こちらによると、認証スコープが足りていない。
インスタンスの設定を確認すると、「デフォルトのアクセス権を許可」になっていたので、以下のように「すべての Cloud API に完全アクセス権を許可」に変更して対処した(設定変更時はインスタンスを停止する必要がある)。

APIアクセスを許可
APIアクセスを許可

kubectlでGKEクラスタに接続する。

クラスタに接続出来ているが、WARNINGメッセージが出る
$ kubectl get node
W1016 13:50:03.312107   20207 gcp.go:119] WARNING: the gcp auth plugin is deprecated in v1.22+, unavailable in v1.26+; use gcloud instead.
To learn more, consult https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke
NAME                                       STATUS   ROLES    AGE   VERSION
gke-cluster-1-default-pool-71e862dd-7b78   Ready    <none>   3d    v1.25.1-gke.500
gke-cluster-1-default-pool-71e862dd-lbk8   Ready    <none>   3d    v1.25.1-gke.500
gke-cluster-1-default-pool-71e862dd-rh0q   Ready    <none>   3d    v1.25.1-gke.500

WARNINGメッセージで案内された「Here's what to know about changes to kubectl authentication coming in GKE v1.26」を見ると、kubectl がgke-gcloud-auth-pluginを使うようにするためには、設定が必要とのこと。
記載されている手順を実行してみる。

環境変数を~/.bashrcに記載
$ echo "export USE_GKE_GCLOUD_AUTH_PLUGIN=True" >> ~/.bashrc

変更を反映
$ source ~/.bashrc

gcloudのコンポーネントを最新版に更新
$ gcloud components update

再度クラスタの認証情報を取得
$ gcloud container clusters get-credentials cluster-1 --region us-west1-a
Fetching cluster endpoint and auth data.
kubeconfig entry generated for cluster-1.

WARNINGが出なくなった
$ kubectl get node
NAME                                       STATUS   ROLES    AGE   VERSION
gke-cluster-1-default-pool-71e862dd-7b78   Ready    <none>   3d    v1.25.1-gke.500
gke-cluster-1-default-pool-71e862dd-lbk8   Ready    <none>   3d    v1.25.1-gke.500
gke-cluster-1-default-pool-71e862dd-rh0q   Ready    <none>   3d    v1.25.1-gke.500

kubectlの自動補完とエイリアスの設定をする。

$ sudo yum install -y bash-completion
$ source <(kubectl completion bash)
$ echo "source <(kubectl completion bash)" >> ~/.bashrc
$ echo 'alias k=kubectl' >> ~/.bashrc
$ echo 'complete -F __start_kubectl k' >> ~/.bashrc
$ exit

もう一度ログインすると、kubectlでもkでもtabでのコマンド補完が有効になっている。

$ kubectl
alpha          autoscale      create         exec           logs           rollout        version
annotate       certificate    debug          explain        options        run            wait
api-resources  cluster-info   delete         expose         patch          scale
api-versions   completion     describe       get            plugin         set
apply          config         diff           help           port-forward   taint
attach         cordon         drain          kustomize      proxy          top
auth           cp             edit           label          replace        uncordon

$ k
alpha          autoscale      create         exec           logs           rollout        version
annotate       certificate    debug          explain        options        run            wait
api-resources  cluster-info   delete         expose         patch          scale
api-versions   completion     describe       get            plugin         set
apply          config         diff           help           port-forward   taint
attach         cordon         drain          kustomize      proxy          top
auth           cp             edit           label          replace        uncordon

まとめ

GCP Compute Engine インスタンス(無料枠)を作成してSSH接続する

Google Cloud Platform(GCP)のIaaSサービスであるGoogle Compute Engine(GCE)に検証用途でVMインスタンスを作成し、SSH接続をしたときのメモ。

環境

VMインスタンスのOS:CentOS7

実施内容

インスタンスを作成する

GCPのドキュメントを参考に、VMインスタンスを作成する。
GCEの無料枠に収まるよう、以下の内容で作成することにした。
※リージョンはアイオワ(us-central1)、サウスカロライナ(us-east1)も対象だが、東京からの距離が最も近いオレゴンを選択した。

  • リージョン:オレゴン(us-west1)
  • ストレージ:30GBの標準永続ディスク
  • インスタンスタイプ:非プリエンプティブル e2-micro

GCEのページで「インスタンスを作成」をクリックする。
作成するインスタンスの設定画面になるので、上記の無料枠条件に当てはまるよう、以下のようにリージョン、ゾーン、マシンの構成、ブートディスク、アクセススコープの設定を変更する。

  • 今回はCentOS7を使いたいので、ブートディスクのイメージのところは「CentOS7」を選択する。
  • アクセススコープは「デフォルトのアクセス権を許可」だと実行出来ない操作が出てくるので、色々検証できるよう「すべての Cloud API に完全アクセス権を許可」に変更する。

インスタンスの作成1
インスタンスの作成1

インスタンスの作成2
インスタンスの作成2

設定を変更したら、画面一番下の「作成」をクリックする。
すると、以下のようにVMインスタンスが作成される。

作成されたインスタンス
作成されたインスタンス

インスタンスの外部IPを固定する

インスタンスには外部IP(グローバルIP)が割り当てられているが、これはエフェメラル外部IPで、インスタンスの再起動などで変わってしまう。
固定の方が使うときに便利なので、こちらを参考に外部IPをエフェメラルから静的IPに昇格させる。

ナブゲーションメニューからVPCネットワークの画面に遷移し、「IPアドレス」をクリックする。
以下のようにIPアドレスの一覧が表示される。
先ほど作成したinstance-1に割り当てられた外部IPがあるので、一番右の「予約」をクリックする。

IPの情報
IPの情報

静的IPアドレス名を適当に付けて「予約」をクリックする。

静的アドレスの予約
静的アドレスの予約

すると、以下のようにIPの種類列の表示が「エフェメラル」から「静的」に変わる。
これで外部IPを固定化できた。

種類が「エフェメラル」から「静的」に変わっている
種類が「エフェメラル」から「静的」に変わっている

インスタンスファイアウォール設定

インスタンスの一覧のIP列にある「nic0」をクリックすると、ネットワーク インターフェースの詳細を確認することができる。
ここで、デフォルトで設定されているファイアウォールのルールを見ると、以下のようにicmp、ssh、RDP接続がどこからでも可能な設定となっている。

デフォルトのファイアウォールルール
デフォルトのファイアウォールルール

これだとセキュリティ的に不安なので、各ルールをクリックして編集し、送信元IPv4範囲が「0.0.0.0/0」となっている箇所を自分が使うソースIPに書き換えておく。

SSH接続の設定をする

こちらによると、インスタンスへのSSH接続は「OS Login」を使うのがおすすめとのこと。
しかし今回は検証用途で、簡単に設定・接続したいのでメタデータ内の SSH 認証鍵を使う方法でやってみる。

SSH 認証鍵を作成する」を参考に、SSH認証鍵を作成する。
適当なLinuxサーバで以下を実行する。

認証鍵ペアを作成する
# ssh-keygen -t rsa -f ~/.ssh/gce-ssh-key -C dev-user -b 2048
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/gce-ssh-key.
Your public key has been saved in /root/.ssh/gce-ssh-key.pub.
The key fingerprint is:
SHA256:jBio6FJ/0o4KC7B5KD1JaZIFDJyRKPftwiTT6wKPMR0 dev-user
The key's randomart image is:
+---[RSA 2048]----+
|B.+              |
|o*..             |
|..oo..           |
|.+E.+o.o         |
|*.=*.o. S        |
|*X.o+..          |
|OB=.o.o          |
|=+o..=           |
|. .o. .          |
+----[SHA256]-----+

秘密鍵を確認
#ls -l ~/.ssh/gce-ssh-key
-rw------- 1 root root 1679 10月 14 23:30 /root/.ssh/gce-ssh-key

公開鍵を確認
#ls -l ~/.ssh/gce-ssh-key.pub
-rw-r--r-- 1 root root 390 10月 14 23:30 /root/.ssh/gce-ssh-key.pub
#cat /root/.ssh/gce-ssh-key.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDl7Yvw3k2R7wE/CKhF3etihDLijUVZGlEsIUYjvhny+B/z6eBBn3I2qJ7bPp2yfhh3PQYbb+o2mr/0nQhK0xKIzjNMK0Gqmii3He1nYRb27nFltQ9uAjHE/kkNax1tder9nruAv9YVjahP1raATUUCsiNEGYusS+TjpYY/+wFl74g2JyPMUknSwKpt1Ig+4Pmg1CtFiiDA1bdURSXdFIxOqYp5iZEwtepJne3sWgxqh5hAEtMP9SpArKHkuTUOLmyx3/ArEv5Fr4Fkp8wracc9MlBuDsUM5ih2z25mYGaKKCXEbA5sdy2O1vtOiaXAvPMTYLwv7VMmCkGFE5/bywuf dev-user

次に、「インスタンス メタデータに SSH 認証鍵を追加する」を参考に、インスタンスに公開鍵を登録する。

対象のインスタンスの詳細ページを表示し、「編集」をクリックする。

インスタンスの詳細
インスタンスの詳細

「セキュリティとアクセス」のSSH 認証鍵の「項目の追加」をクリックし、以下のように公開鍵(gce-ssh-key.pub)の中身を記載する。
その後、ページ下部の「保存」をクリックする。

インスタンスに公開鍵を登録
インスタンスに公開鍵を登録

これでインスタンスに公開鍵が登録されたので、秘密鍵を指定して外部IPに対しSSH接続してみる。

#ssh -i ~/.ssh/gce-ssh-key dev-user@34.168.77.125
Warning: Permanently added '34.168.77.125' (ECDSA) to the list of known hosts.
[dev-user@instance-1 ~]$ cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

→無事接続出来た。

gcloud CLI の設定

インスタンスには、最初からgcloud CLIがインストールされている。

$ gcloud version
Google Cloud SDK 404.0.0
alpha 2022.09.23
beta 2022.09.23
bq 2.0.78
bundled-python3-unix 3.9.12
core 2022.09.23
gcloud-crc32c 1.0.0
gsutil 5.14

しかし、このgcloud CLIだとコンポーネントのインストールやアップデートが出来ない。
(以下のように、代わりにyumを使うように言われてしまう。)

gcloud CLIでkubectlをインストールしてみる
$ gcloud components install kubectl
ERROR: (gcloud.components.install)
You cannot perform this action because the Google Cloud CLI component manager
is disabled for this installation. You can run the following command
to achieve the same result for this installation:

sudo yum install kubectl

gcloud CLIでコンポーネントをアップデートする
$ gcloud components update
To help improve the quality of this product, we collect anonymized usage data and anonymized stacktraces when
crashes are encountered; additional information is available at <https://cloud.google.com/sdk/usage-statistics>.
This data is handled in accordance with our privacy policy <https://cloud.google.com/terms/cloud-privacy-notice>.
 You may choose to opt in this collection now (by choosing 'Y' at the below prompt), or at any time in the future
 by running the following command:

    gcloud config set disable_usage_reporting false

Do you want to opt-in (y/N)?  y

Beginning update. This process may take several minutes.
ERROR: (gcloud.components.update)
You cannot perform this action because the Google Cloud CLI component manager
is disabled for this installation. You can run the following command
to achieve the same result for this installation:

sudo yum makecache && sudo yum update google-cloud-sdk-app-engine-go google-cloud-sdk-kpt google-cloud-sdk-kubectl-oidc google-cloud-sdk-harbourbridge google-cloud-sdk-firestore-emulator google-cloud-sdk-pubsub-emulator google-cloud-sdk-datastore-emulator google-cloud-sdk-spanner-emulator google-cloud-sdk-config-connector google-cloud-sdk google-cloud-sdk-terraform-tools google-cloud-sdk-cbt google-cloud-sdk-anthos-auth google-cloud-sdk-app-engine-python google-cloud-sdk-cloud-build-local google-cloud-sdk-nomos google-cloud-sdk-gke-gcloud-auth-plugin google-cloud-sdk-datalab google-cloud-sdk-bigtable-emulator kubectl google-cloud-sdk-app-engine-python-extras google-cloud-sdk-skaffold google-cloud-sdk-cloud-run-proxy google-cloud-sdk-bundled-python3 google-cloud-sdk-package-go-module google-cloud-sdk-local-extract google-cloud-sdk-app-engine-grpc google-cloud-sdk-minikube google-cloud-sdk-app-engine-java

こちらに以下のように記載されている。
最初から入っているgcloud CLIは恐らくyumでインストールされたものだと思われる。

注: APT や yum などのパッケージ マネージャーを使用して gcloud CLI をインストールしている場合、gcloud CLI コンポーネント マネージャーの手順は動作しません。この方法でインストールした場合には gcloud CLIコンポーネント・マネージャが無効になっているためです。代わりに、こちらのパッケージを使用してください。

以下を見ると、gcloud CLIコンポーネント・マネージャを使用したい場合には、パッケージ マネージャーでインストールされたgcloud CLIをアンインストールし、改めてインストーラーを使ってインストールすれば良いらしい。
stackoverflow.com GCPのドキュメントでは、基本的にコンポーネントはgcloud CLIでインストールしているので、gcloud CLIコンポーネント・マネージャを使用出来る状態にしておきたい。
やってみる。

gcloud CLIをアンインストールする。

アンインストール
$ sudo yum remove google-cloud-sdk

アンインストールされたことを確認
$ gcloud version
-bash: /usr/bin/gcloud: そのようなファイルやディレクトリはありません

こちらの手順を参考に、gcloud CLIをインストールする。
前提としてPython3(3.5~3.9)が必要なため、インストールする。

pyenvのインストールする。

yumアップデート
$ sudo yum -y update

必要なツールをインストール
$ sudo yum -y install gcc zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel openssl-devel tk-devel libffi-devel git make

pyenvをgit clone
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv

PATHを設定
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
$ echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(pyenv init -)"' >> ~/.bashrc

設定を確認
$ tail -n 3 ~/.bashrc
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

設定変更を反映
$ source ~/.bashrc

pyenvがインストールされたことを確認
$ pyenv --version
pyenv 2.3.5-2-g03a5d65

Python3.9をインストールする。

インストールできるPythonバージョンを確認
$ pyenv install --list

Python3.9.15をインストール
$ pyenv install 3.9.15
Downloading Python-3.9.15.tar.xz...
-> https://www.python.org/ftp/python/3.9.15/Python-3.9.15.tar.xz
Installing Python-3.9.15...
WARNING: The Python lzma extension was not compiled. Missing the lzma lib?
Installed Python-3.9.15 to /home/dev-user/.pyenv/versions/3.9.15

実行するPythonをPython3.9に設定する。

現在のPythonバージョンを確認
$ pyenv versions
* system (set by /home/dev-user/.pyenv/version)
  3.9.15
$ python -V
Python 2.7.5

バージョンを切り替え
$ pyenv global 3.9.15

切り替え後のPythonバージョンを確認
$ pyenv versions
  system
* 3.9.15 (set by /home/dev-user/.pyenv/version)
$ python -V
Python 3.9.15

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

gcloud CLIパッケージをダウンロード
$ curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-404.0.0-linux-x86_64.tar.gz

展開する
$ tar -xf google-cloud-cli-404.0.0-linux-x86_64.tar.gz

gcloud CLIのPATHを設定
$ ./google-cloud-sdk/install.sh \
  --usage-reporting false \
  --rc-path ~/.bashrc \
  --command-completion true \
  --path-update true

設定を確認
$ tail -n 5  ~/.bashrc
# The next line updates PATH for the Google Cloud SDK.
if [ -f '/home/dev-user/google-cloud-sdk/path.bash.inc' ]; then . '/home/dev-user/google-cloud-sdk/path.bash.inc'; fi

# The next line enables shell command completion for gcloud.
if [ -f '/home/dev-user/google-cloud-sdk/completion.bash.inc' ]; then . '/home/dev-user/google-cloud-sdk/completion.bash.inc'; fi

設定変更を反映
$ source ~/.bashrc

インストールされたgcloud CLIを確認
$ gcloud --version
Google Cloud SDK 404.0.0
bq 2.0.78
bundled-python3-unix 3.9.12
core 2022.09.23
gcloud-crc32c 1.0.0
gsutil 5.14

※install.sh実行時のオプションの意味は以下。

  • --usage-reporting:gcloud CLI の改善のために匿名の使用統計情報を送信するかどうか。
  • --rc-path:更新するプロファイルの場所
  • --command-completion:コマンド補完を有効にするかどうか。
  • --path-update:プロファイルのPATHを更新するかどうか。

※最初からインストールされていたgcloud CLIのプロパティが残っているので、初期化(gcloud init)は実行しなかった。

 

これで、gcloud CLIコンポーネントのインストールやアップデートが出来るようになる。

gcloud CLIでkubectlをインストールしてみる
$ gcloud components install kubectl

インストールできた
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"22+", GitVersion:"v1.22.14-dispatcher-dirty", GitCommit:"009f3ac5f1d09b1ec5d4dc811b6032271fe61f75", GitTreeState:"dirty", BuildDate:"2022-09-19T01:31:36Z", GoVersion:"go1.16.15", Compiler:"gc", Platform:"linux/amd64"}
The connection to the server localhost:8080 was refused - did you specify the right host or port?

gcloud CLIでコンポーネントをアップデートする
$ gcloud components update

おまけ:インスタンスの初期設定

検証用途なので、検証の邪魔になりがちなOSファイアウォールSELinuxは停止・無効化しておく。

OSファイアウォールを無効化する。

$ sudo systemctl stop firewalld

$ sudo systemctl disable firewalld
Removed symlink /etc/systemd/system/multi-user.target.wants/firewalld.service.
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.

$ sudo systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
   Active: inactive (dead)
     Docs: man:firewalld(1)

$ sudo systemctl is-enabled firewalld
disabled

SELinuxを無効化する

$ getenforce
Enforcing

$ sudo sed -i".org" -e "s/^SELINUX=enforcing$/SELINUX=disabled/g" /etc/selinux/config

$ sudo reboot

$ getenforce
Disabled

参考文献

pyenv
Amazon Linux 2環境上にGCPコマンドラインツール・Google Cloud SDK動作環境を整える
SELinux無効化一行コマンド

Prometheus Node Exporter をAnsibleでインストールする(Rocky Linux 8)

PrometheusのExporterのひとつである、Node ExporterをAnsibleでインストールした時のメモ。
PrometheusやNode Exporterの設定は以下記事に記載してある。

tk-ch.hatenablog.com

環境

  • Node Exporter:1.4.0
  • 管理対象サーバ(Ansibleの実行先)のOS:RockyLinux8.6
  • ansible:6.4.0
  • ansible-core:2.13.4

実施内容

Ansibleのplaybookを準備

以下のディレクトリ、ファイルを作成した。
ディレクトリ構成は、Ansible公式ドキュメントのBest Practiceを参考にしている。

# tree --charset=c ansible
ansible
|-- ansible.cfg
|-- inventories
|   `-- production
|       |-- group_vars
|       |   `-- testservers.yml
|       `-- hosts
|-- roles
|   `-- install_node_exporter
|       |-- defaults
|       |   `-- main.yml
|       |-- files
|       |   `-- node_exporter-1.4.0.linux-amd64.tar.gz
|       |-- tasks
|       |   `-- main.yml
|       `-- templates
|           |-- node_exporter.service.j2
|           `-- node_exporter_envfile.j2
|-- site.yml
`-- testservers.yml

ansible/ansible.cfg
ansible実行時の設定。Ansibleが管理対象サーバへSSHする際に、ホスト鍵のチェックをしないようにしている。

[defaults]
host_key_checking = False

ansible/inventories/production/group_vars/testservers.yml
管理対象サーバへのアクセス情報を記載。

---
ansible_ssh_user: root
ansible_ssh_pass: password

ansible/inventories/production/hosts
管理対象サーバの接続先を記載。

[testservers]
192.168.10.123

roles/install_node_exporter/defaults/main.yml このロールで使う変数を定義している。

---
work_dir: /root/ansible_work/node_exporter      # このタスクの作業用ディレクトリ
node_exporter_user: node_exporter               # Node Exporterの実行ユーザ
node_exporter_group: "{{ node_exporter_user }}" # Node Exporterの実行ユーザの所属グループ
node_exporter_id: 65541                         # Node Exporterの実行ユーザ・グループのID(任意の値でよい)
node_exporter_version: 1.4.0                    # インストールするNode Exporterのバージョン
prometheus_dir: /opt/prometheus                 # Node Exporterの配置先
node_exporter_bin: /usr/local/bin               # Node Exporterの実行ファイルリンク配置先
node_exporter_envfile: /etc/sysconfig/node_exporter  # Node Exporterの変数定義ファイル
textfile_collector_dir: /var/lib/node_exporter/textfile_collector  # Textfile Collector用ファイルの配置先

ansible/roles/install_node_exporter/tasks/main.yml
実行するタスクを記載。Node Exporterをインストール、起動する内容。

---
- name: create work directory
  file:
    path: "{{ work_dir }}"
    state: directory
    owner: root
    group: root
    mode: 0755

- name: add group for node exporter
  group:
    name: "{{ node_exporter_group }}"
    gid: "{{ node_exporter_id }}"

- name: add user for node exporter
  user:
    name: "{{ node_exporter_user }}"
    group: "{{ node_exporter_group }}"
    uid: "{{ node_exporter_id }}"
    create_home: no
    shell: /sbin/nologin

- name: copy node exporter tarball
  copy:
    src: "node_exporter-{{ node_exporter_version }}.linux-amd64.tar.gz"
    dest: "{{ work_dir }}"

- name: create prometheus directory
  file:
    path: "{{ prometheus_dir }}"
    state: directory
    owner: root
    group: root
    mode: "0755"

- name: extract node exporter tarball
  unarchive:
    remote_src: yes
    src: "{{ work_dir }}/node_exporter-{{ node_exporter_version }}.linux-amd64.tar.gz"
    dest: "{{ prometheus_dir }}"
    owner: "{{ node_exporter_user }}"
    group: "{{ node_exporter_group }}"

- name: create node exporter symbolic link
  file:
    path: "{{ node_exporter_bin }}/node_exporter"
    state: link
    src: "{{ prometheus_dir }}/node_exporter-{{ node_exporter_version }}.linux-amd64/node_exporter"

- name: create node exporter systemd file
  template:
    src: node_exporter.service.j2
    dest: /etc/systemd/system/node_exporter.service
    owner: root
    group: root
    mode: 0664

- name: create node exporter environment directory
  template:
    src: node_exporter_envfile.j2
    dest: "{{ node_exporter_envfile }}"
    owner: root
    group: root
    mode: 0664

- name: create textfile collector directory
  file:
    path: "{{ textfile_collector_dir }}"
    state: directory
    owner: "{{ node_exporter_user }}"
    group: "{{ node_exporter_group }}"
    mode: 0755

- name: enable and start node_exporter
  systemd:
    daemon_reload: yes
    enabled: yes
    state: started
    name: node_exporter.service

roles/install_node_exporter/files/ 管理対象サーバにアップロードする用のファイルを置く。
今回はインストール対象のNode Exporterのtarballを事前にダウンロードして配置している。
tarballはここからダウンロードできる。

# ls -l roles/install_node_exporter/files/
合計 9876
-rw-r--r-- 1 root root 10111972  9月 26 12:39 node_exporter-1.4.0.linux-amd64.tar.gz

roles/install_node_exporter/templates/node_exporter.service.j2
Node ExporterのSysytemdService用テンプレートファイル。

[Unit]
Description=Node Exporter

[Service]
User={{ node_exporter_user }}
EnvironmentFile=-{{ node_exporter_envfile }}
ExecStart={{ node_exporter_bin }}/node_exporter $OPTIONS

[Install]
WantedBy=multi-user.target

roles/install_node_exporter/templates/node_exporter_envfile.j2
Node Exporter実行時のオプション設定ファイルのテンプレートファイル。

OPTIONS="--collector.textfile.directory {{ textfile_collector_dir }}"

ansible/site.yml
実行するplaybook。個別のplaybookをimportして使う。

---
- import_playbook: testservers.yml

ansible/testservers.yml
個別のplaybook。

---
- hosts: testservers
  roles:
    - install_node_exporter

ansibleの実行

ansible-playbookを実行する。

# ansible-playbook -i inventories/production/hosts site.yml

管理対象のサーバに接続し、node_exporterサービスが正常に起動していればOK。

# systemctl status node_exporter
● node_exporter.service - Node Exporter
   Loaded: loaded (/etc/systemd/system/node_exporter.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2022-10-11 11:27:43 UTC; 1h 42min ago
 Main PID: 36046 (node_exporter)
    Tasks: 5 (limit: 10992)
   Memory: 18.7M
   CGroup: /system.slice/node_exporter.service
           mq36046 /usr/local/bin/node_exporter --collector.textfile.directory /var/lib/node_exporter/textfile_collector

10月 11 11:27:43 ip-192-168-10-123.ap-northeast-1.compute.internalmydomain.local node_exporter[36046]: ts=2022-10-11T11:27:43.337Z caller=node_exporter.go:115 level=info collector=thermal_zone
10月 11 11:27:43 ip-192-168-10-123.ap-northeast-1.compute.internalmydomain.local node_exporter[36046]: ts=2022-10-11T11:27:43.337Z caller=node_exporter.go:115 level=info collector=time
10月 11 11:27:43 ip-192-168-10-123.ap-northeast-1.compute.internalmydomain.local node_exporter[36046]: ts=2022-10-11T11:27:43.337Z caller=node_exporter.go:115 level=info collector=timex
10月 11 11:27:43 ip-192-168-10-123.ap-northeast-1.compute.internalmydomain.local node_exporter[36046]: ts=2022-10-11T11:27:43.337Z caller=node_exporter.go:115 level=info collector=udp_queues
10月 11 11:27:43 ip-192-168-10-123.ap-northeast-1.compute.internalmydomain.local node_exporter[36046]: ts=2022-10-11T11:27:43.337Z caller=node_exporter.go:115 level=info collector=uname
10月 11 11:27:43 ip-192-168-10-123.ap-northeast-1.compute.internalmydomain.local node_exporter[36046]: ts=2022-10-11T11:27:43.337Z caller=node_exporter.go:115 level=info collector=vmstat
10月 11 11:27:43 ip-192-168-10-123.ap-northeast-1.compute.internalmydomain.local node_exporter[36046]: ts=2022-10-11T11:27:43.337Z caller=node_exporter.go:115 level=info collector=xfs
10月 11 11:27:43 ip-192-168-10-123.ap-northeast-1.compute.internalmydomain.local node_exporter[36046]: ts=2022-10-11T11:27:43.337Z caller=node_exporter.go:115 level=info collector=zfs
10月 11 11:27:43 ip-192-168-10-123.ap-northeast-1.compute.internalmydomain.local node_exporter[36046]: ts=2022-10-11T11:27:43.338Z caller=node_exporter.go:199 level=info msg="Listening on" address=:9100
10月 11 11:27:43 ip-192-168-10-123.ap-northeast-1.compute.internalmydomain.local node_exporter[36046]: ts=2022-10-11T11:27:43.338Z caller=tls_config.go:195 level=info msg="TLS is disabled." http2=false

まとめ

  • RockyLinux8へのPrometheus Node ExporterのインストールをAnsibleで自動化できた。

参考文献

Ansibleの実行環境をDockerコンテナにする

Ansibleを使う際に、以下のような環境面での悩みが出てくるケースがある。

  • 複数の構成管理サーバにAnsibleをインストールするのが手間。
  • Ansibleの実行環境にはPythonが必要だが、Ansibleのために特定バージョンのPythonをインストールして環境が汚れるのが嫌。
  • 複数のバージョンのAnsibleを同じサーバで実行させたい場合、共存させるのが面倒(というか共存できない?)。

→これらは、Ansibleの実行環境をDockerコンテナにすれば解決できる。
 という訳で、Ansible実行用のDockerコンテナを用意してみる。

環境

  • DockerホストのOS:RockyLinux8.6
  • 管理対象サーバ(Ansibleの実行先)のOS:RockyLinux8.6
  • Docker:20.10.18
  • ansible:7.3.0
  • ansible-core:2.14.3

実施内容

Ansibleの最新バージョンを確認

公式ドキュメントのAnsible Roadmapによると、2023年3月現在の最新安定版のバージョンは7系。
pipを使って、インストールできる最新版を確認する。

# docker run --rm -it rockylinux:8.6 /bin/bash

# dnf module -y install python39

# python3 -m pip install ansible==
WARNING: Running pip install with root privileges is generally not a good idea. Try `python3 -m pip install --   user` instead.
ERROR: Could not find a version that satisfies the requirement ansible== (from versions: 1.0, 1.1, 1.2, 1.2.1,    1.2.2, 1.2.3, 1.3.0, 1.3.1, 1.3.2, 1.3.3, 1.3.4, 1.4, 1.4.1, 1.4.2, 1.4.3, 1.4.4, 1.4.5, 1.5, 1.5.1, 1.5.2, 1   .5.3, 1.5.4, 1.5.5, 1.6, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5, 1.6.6, 1.6.7, 1.6.8, 1.6.9, 1.6.10, 1.7, 1.7.1, 1.   7.2, 1.8, 1.8.1, 1.8.2, 1.8.3, 1.8.4, 1.9.0.1, 1.9.1, 1.9.2, 1.9.3, 1.9.4, 1.9.5, 1.9.6, 2.0.0.0, 2.0.0.1, 2.0   .0.2, 2.0.1.0, 2.0.2.0, 2.1.0.0, 2.1.1.0, 2.1.2.0, 2.1.3.0, 2.1.4.0, 2.1.5.0, 2.1.6.0, 2.2.0.0, 2.2.1.0, 2.2.2   .0, 2.2.3.0, 2.3.0.0, 2.3.1.0, 2.3.2.0, 2.3.3.0, 2.4.0.0, 2.4.1.0, 2.4.2.0, 2.4.3.0, 2.4.4.0, 2.4.5.0, 2.4.6.0   , 2.5.0a1, 2.5.0b1, 2.5.0b2, 2.5.0rc1, 2.5.0rc2, 2.5.0rc3, 2.5.0, 2.5.1, 2.5.2, 2.5.3, 2.5.4, 2.5.5, 2.5.6, 2.   5.7, 2.5.8, 2.5.9, 2.5.10, 2.5.11, 2.5.12, 2.5.13, 2.5.14, 2.5.15, 2.6.0a1, 2.6.0a2, 2.6.0rc1, 2.6.0rc2, 2.6.0   rc3, 2.6.0rc4, 2.6.0rc5, 2.6.0, 2.6.1, 2.6.2, 2.6.3, 2.6.4, 2.6.5, 2.6.6, 2.6.7, 2.6.8, 2.6.9, 2.6.10, 2.6.11,    2.6.12, 2.6.13, 2.6.14, 2.6.15, 2.6.16, 2.6.17, 2.6.18, 2.6.19, 2.6.20, 2.7.0.dev0, 2.7.0a1, 2.7.0b1, 2.7.0rc   1, 2.7.0rc2, 2.7.0rc3, 2.7.0rc4, 2.7.0, 2.7.1, 2.7.2, 2.7.3, 2.7.4, 2.7.5, 2.7.6, 2.7.7, 2.7.8, 2.7.9, 2.7.10,    2.7.11, 2.7.12, 2.7.13, 2.7.14, 2.7.15, 2.7.16, 2.7.17, 2.7.18, 2.8.0a1, 2.8.0b1, 2.8.0rc1, 2.8.0rc2, 2.8.0rc   3, 2.8.0, 2.8.1, 2.8.2, 2.8.3, 2.8.4, 2.8.5, 2.8.6, 2.8.7, 2.8.8, 2.8.9, 2.8.10, 2.8.11, 2.8.12, 2.8.13, 2.8.1   4, 2.8.15, 2.8.16rc1, 2.8.16, 2.8.17rc1, 2.8.17, 2.8.18rc1, 2.8.18, 2.8.19rc1, 2.8.19, 2.8.20rc1, 2.8.20, 2.9.   0b1, 2.9.0rc1, 2.9.0rc2, 2.9.0rc3, 2.9.0rc4, 2.9.0rc5, 2.9.0, 2.9.1, 2.9.2, 2.9.3, 2.9.4, 2.9.5, 2.9.6, 2.9.7,    2.9.8, 2.9.9, 2.9.10, 2.9.11, 2.9.12, 2.9.13, 2.9.14rc1, 2.9.14, 2.9.15rc1, 2.9.15, 2.9.16rc1, 2.9.16, 2.9.17   rc1, 2.9.17, 2.9.18rc1, 2.9.18, 2.9.19rc1, 2.9.19, 2.9.20rc1, 2.9.20, 2.9.21rc1, 2.9.21, 2.9.22rc1, 2.9.22, 2.   9.23rc1, 2.9.23, 2.9.24rc1, 2.9.24, 2.9.25rc1, 2.9.25, 2.9.26rc1, 2.9.26, 2.9.27rc1, 2.9.27, 2.10.0a1, 2.10.0a   2, 2.10.0a3, 2.10.0a4, 2.10.0a5, 2.10.0a6, 2.10.0a7, 2.10.0a8, 2.10.0a9, 2.10.0b1, 2.10.0b2, 2.10.0rc1, 2.10.0   , 2.10.1, 2.10.2, 2.10.3, 2.10.4, 2.10.5, 2.10.6, 2.10.7, 3.0.0b1, 3.0.0rc1, 3.0.0, 3.1.0, 3.2.0, 3.3.0, 3.4.0   , 4.0.0a1, 4.0.0a2, 4.0.0a3, 4.0.0a4, 4.0.0b1, 4.0.0b2, 4.0.0rc1, 4.0.0, 4.1.0, 4.2.0, 4.3.0, 4.4.0, 4.5.0, 4.   6.0, 4.7.0, 4.8.0, 4.9.0, 4.10.0, 5.0.0a1, 5.0.0a2, 5.0.0a3, 5.0.0b1, 5.0.0b2, 5.0.0rc1, 5.0.1, 5.1.0, 5.2.0,    5.3.0, 5.4.0, 5.5.0, 5.6.0, 5.7.0, 5.7.1, 5.8.0, 5.9.0, 5.10.0, 6.0.0a1, 6.0.0a2, 6.0.0a3, 6.0.0b1, 6.0.0b2, 6   .0.0rc1, 6.0.0, 6.1.0, 6.2.0, 6.3.0, 7.3.0, 6.5.0, 6.6.0, 6.7.0, 7.0.0a1, 7.0.0a2, 7.0.0b1, 7.0.0rc1, 7.0.0, 7   .1.0, 7.2.0, 7.3.0)
ERROR: No matching distribution found for ansible==

→7.3.0が最新なようなので、これを使うことにする。

Ansible実行用のDockerイメージをビルドする

Dockerfileを作成する。 Dockerfile_ansible

# ベースイメージ
FROM rockylinux:8.6

# 変数定義
ARG ANSIBLE_VERSION

# 必要なパッケージのインストール
RUN dnf module -y install python39 && \
    dnf -y install openssh-clients \
    sshpass && \
    dnf clean all && \
    rm -rf /var/cache/dnf/*

# Ansibleのインストール
RUN python3 -m pip install --no-cache-dir ansible==$ANSIBLE_VERSION

# ロケールを日本語に設定
RUN dnf -y install glibc-locale-source glibc-langpack-en && \
    dnf clean all && \
    rm -rf /var/cache/dnf/*
RUN localedef -f UTF-8 -i ja_JP ja_JP.utf8
RUN echo 'LANG="ja_JP.UTF-8"' >  /etc/locale.conf

# タイムゾーンをJSTに設定
RUN echo 'ZONE="Asia/Tokyo"' > /etc/sysconfig/clock
RUN rm -f /etc/localtime
RUN ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# コンテナログイン時のカレントディレクトリを設定
WORKDIR /work/ansible

→Dockerfileの解説

  • ベースイメージがRockyLinux8.6なのは使い慣れているからで、特に意味は無い。他のOSで作っても良い。
  • Ansibleのインストールコマンドは、Ansible公式ドキュメントのここを参考にしている。
  • openssh-clientsは、Ansibleから管理対象サーバへ接続するために必要なのでインストールした。
  • sshpassは、管理対象サーバへ接続する際にパスワード認証を使う場合は必要なため、インストールした。
  • Ansibleで使うモジュールによっては他にもパッケージが必要になるので、適宜追加すること。
  • インストールするAnsibleのバージョンを制御できるよう、ARGにて変数ANSIBLE_VERSIONを定義し、Ansibleインストール時に指定している。 変数に設定する値は、後ほど「docker build」実行時に--build-argオプションで指定する。
    詳細はDockerの公式ドキュメントのここを参照。
  • dnf実行時の書き方については、RedHatの「How to build tiny container images」を参考にした。
  • ロケールタイムゾーンの設定は必須ではないが、自前のコンテナイメージ作成時はいつもやっているので入れている。

Ansibleバージョンを7.3.0と指定し、コンテナイメージをビルドする。

インストールしたいAnsibleのバージョンを環境変数に格納
# export ANSIBLE_VERSION=7.3.0

Dockerイメージをビルド
# docker build --no-cache=true -f Dockerfile_ansible -t ansible:$ANSIBLE_VERSION --build-arg ANSIBLE_VERSION=$ANSIBLE_VERSION .

ビルドされたイメージを確認
# docker images | grep ansible | grep $ANSIBLE_VERSION
ansible               7.3.0     ac001dbb28c2   2 minutes ago   657MB

ビルドされたイメージを起動し、想定通りのAnsibleバージョンがインストールされたことを確認
# docker run --rm -it ansible:$ANSIBLE_VERSION python3 -m pip freeze | grep ansible
ansible==7.3.0
ansible-core==2.13.4

ビルドしたイメージを使ってansible-playbookを実行する

Dockerホスト(上記イメージを起動するサーバ)にansibleで使うファイルを準備する。
今回は、Dockerホストの/root/ansibleディレクリに以下のように配置した。
ディレクトリ構成は、Ansible公式ドキュメントのBest Practiceを参考にしている。

# cd /root
# tree --charset=c ansible
ansible
|-- ansible.cfg
|-- inventories
|   `-- production
|       |-- group_vars
|       |   `-- testservers.yml
|       `-- hosts
|-- roles
|   `-- test
|       |-- files
|       |   `-- test.txt
|       `-- tasks
|           `-- main.yml
|-- site.yml
`-- testservers.yml

ansible/ansible.cfg
ansible実行時の設定。Ansibleが管理対象サーバへSSHする際に、ホスト鍵のチェックをしないようにしている。

[defaults]
host_key_checking = False

ansible/inventories/production/group_vars/testservers.yml
管理対象サーバへのアクセス情報を記載。

---
ansible_ssh_user: root
ansible_ssh_pass: password

ansible/inventories/production/hosts
管理対象サーバの接続先を記載。

[testservers]
192.168.10.123

ansible/roles/test/tasks/main.yml
test.txtというファイルを管理対象サーバの/tmpディレクトリに転送するだけの処理。

---
- name: copy test file
  copy:
    src: "test.txt"
    dest: "/tmp"

ansible/roles/test/files/test.txt

test

ansible/site.yml
実行するplaybook。個別のplaybookをimportして使う。

---
- import_playbook: testservers.yml

ansible/testservers.yml
個別のplaybook。

---
- hosts: testservers
  roles:
    - test

先ほどビルドしたコンテナイメージを指定し、コンテナを起動する。 DockerfileでWORKDIRとして指定したコンテナ内のディレクトリ(今回は「/work/ansible」)に、Dockerホストのansible用ファイル置き場(今回は「/root/ansible」)をマウントする。

# export ANSIBLE_VERSION=7.3.0
# docker run --rm -it -v /root/.ssh/:/root/.ssh/ -v /root/ansible:/work/ansible ansible:$ANSIBLE_VERSION ls -l
total 12
-rw-r--r-- 1 root root 37 Oct 10 23:02 ansible.cfg
drwxr-xr-x 3 root root 24 Oct 10 20:27 inventories
drwxr-xr-x 3 root root 18 Oct 10 21:47 roles
-rw-r--r-- 1 root root 39 Oct 10 20:38 site.yml
-rw-r--r-- 1 root root 45 Oct 10 21:42 testservers.yml

→「docker run」した際のカレントディレクトリから、Dockerホストのansible用ファイルにアクセスできることが確認できる。

ansible-playbookを実行する。

# export ANSIBLE_VERSION=7.3.0
# docker run --rm -it -v /root/.ssh/:/root/.ssh/ -v /root/ansible:/work/ansible ansible:$ANSIBLE_VERSION ansible-playbook -i inventories/production/hosts site.yml

# docker run --rm -it -v /root/.ssh/:/root/.ssh/ -v /root/ansible:/work/ansible ansible:$ANSIBLE_VERSION ansible-playbook -i inventories/production/hosts site.yml

PLAY [testservers] *******************************************************************************************

TASK [Gathering Facts] ***************************************************************************************
ok: [192.168.10.123]

TASK [test : copy test file] *********************************************************************************
changed: [192.168.10.123]

PLAY RECAP ***************************************************************************************************
192.168.10.123               : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

→無事に実行された。

管理対象サーバにSSHして、/tmpディレクトリにtest.txtファイルが転送されていることが確認できる。

# ssh root@192.168.10.123
# cat /tmp/test.txt
test

毎回「docker run ~」を入力するのは手間なので、エイリアスを使って簡単に実行できるようにする。
Dockerホストの「~/.bashrc」に以下の2行を追記する。

export ANSIBLE_VERSION=7.3.0
alias ansible-playbook='docker run --rm -it -v /root/.ssh/:/root/.ssh/ -v /root/ansible:/work/ansible ansible:$ANSIBLE_VERSION ansible-playbook'

試してみる。

変更を反映
# source ~/.bashrc

エイリアスを指定してplaybookを実行する。
# ansible-playbook -i inventories/production/hosts site.yml

PLAY [testservers] *******************************************************************************************

TASK [Gathering Facts] ***************************************************************************************
ok: [192.168.10.123]

TASK [test : copy test file] *********************************************************************************
ok: [192.168.10.123]

PLAY RECAP ***************************************************************************************************
192.168.10.123               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

→「ansible-playbook」を実行するだけでansibleコンテナを使ってansible-playbookを実行できるようになった。
別バージョンのAnsibleも実行したい場合は、該当バージョンのAnsibleがインストールされたDockerイメージを用意し、環境変数「ANSIBLE_VERSION」の値を変えることで実行するバージョンを制御できる。

まとめ

  • Ansibleの実行環境をDockerコンテナにすることができた。Dockerさえインストールされていれば、簡単にAnsibleの実行環境を整備できる。

参考文献

Node ExporterのTextfile Collectorで独自メトリクスを収集する

Node Exporterでハードウェア・OSのメトリクスを収集して、Prometheus、Grafanaで可視化しているのだが、独自メトリクスも可視化したくなった。
Prometheus用のExporterを自作すればよいらしいが、Webサーバも起動する必要があるし、少し面倒そう。
Node ExporterのTextfile Collectorを使えば簡単に独自メトリクスを収集できそうなので、試してみる。

※今回使う環境の構築方法や、Prometheus、Grafana、Node Exporterの設定は以下記事参照。 tk-ch.hatenablog.com

環境

  • OS:RockyLinux8.6
  • Prometheus:v2.37.1
  • Grafana:9.1.6
  • Node Exporter:1.4.0
  • Python:3.9.7
  • prometheus_client:0.14.1

実施内容

Node ExporterのREADMEによると、Node Exporterは、--collector.textfile.directoryオプションで指定したディレクトリ内の「*.prom」という名前のファイルに記載した内容を収集する。
これを利用して独自メトリクスを収集できるようなので、使ってみる。

コマンド実行で試してみる

先ほどのREADMEを参考に、本当に収集されるか試してみる。
Node Exporterが動作している管理対象サーバで、以下を実行する。
(Node Exporterは「--collector.textfile.directory /var/lib/node_exporter/textfile_collector」というオプションで動作している。)

「my_batch_job_completion_time」というメトリクス名で、現在時刻を記載したファイルを作成する
# echo my_batch_job_completion_time $(date +%s) > /var/lib/node_exporter/textfile_collector/my_batch_job.prom.$$
# mv /var/lib/node_exporter/textfile_collector/my_batch_job.prom.$$ /var/lib/node_exporter/textfile_collector/my_batch_job.prom

作成したファイルの中身を確認する
# cat /var/lib/node_exporter/textfile_collector/my_batch_job.prom
my_batch_job_completion_time 1665059541

Node Exporterの収集メトリクスを確認すると「my_batch_job_completion_time」が収集されていることが確認できる
# curl -s http://localhost:9100/metrics | grep my_batch_job_completion_time
# HELP my_batch_job_completion_time Metric read from /var/lib/node_exporter/textfile_collector/my_batch_job.prom
# TYPE my_batch_job_completion_time untyped
my_batch_job_completion_time 1.665059541e+09

→想定通り、ファイルに記載した内容がメトリクスとして収集された。
上記ではコマンドを手動実行したが、実際には独自メトリクスを収集してファイルに記載するスクリプトを定期実行することになる。
スクリプトの例はここにある。

※上記手順では、my_batch_job.promに直接リダイレクトせず、my_batch_job.prom.$$をリネームする形にしている。
これはファイルをアトミックに更新することで、Node Exporterが更新途中のテキストファイルを読み込まないようにするため。
更新途中のデータを読み込むと、エラー発生や不正なデータを収集してしまうリスクがある。
参考:https://www.robustperception.io/atomic-writes-and-the-textfile-collector/

シェルスクリプトで独自メトリクスの値を定期更新する

ランダムな数値をmy_random_numberというメトリクス名で更新するスクリプトを作成する。
メトリクスのタイプについてはここに記載がある。 今回はランダムな数値なので、gaugeにした。

# mkdir /root/script

# vim /root/script/output_random.sh

output_random.sh

#!/bin/bash

# テキストファイルの情報
TEXT_DIR="/var/lib/node_exporter/textfile_collector"
TEXT_BASE="my_random_number"
TEXT_EXT=".prom"
TEXT_PATH=${TEXT_DIR}/${TEXT_BASE}${TEXT_EXT}

# メトリクスの情報
METRICS_NAME="my_random_number"
METRICS_HELP="My random number"
METRICS_TYPE="gauge"

# メトリクスの説明・タイプの出力
echo "# HELP ${METRICS_NAME} ${METRICS_HELP}" > ${TEXT_PATH}.$$
echo "# TYPE ${METRICS_NAME} ${METRICS_TYPE}" >> ${TEXT_PATH}.$$

# メトリクス名と値の出力
echo "my_random_number $RANDOM" >> ${TEXT_PATH}.$$

# テキストファイルの更新
mv ${TEXT_PATH}.$$ ${TEXT_PATH}

実行して、テキストファイルが作成されること、想定通りメトリクス収集されていることを確認する

実行権限を付与
# chmod a+x /root/script/output_random.sh

実行
# /root/script/output_random.sh

テキストファイルを確認
# cat /var/lib/node_exporter/textfile_collector/my_random_number.prom
# HELP my_random_number My random number
# TYPE my_random_number gauge
my_random_number 16633

メトリクスとして収集されていることを確認
# curl -s http://localhost:9100/metrics | grep my_random_number
# HELP my_random_number My random number
# TYPE my_random_number gauge
my_random_number 16633
node_textfile_mtime_seconds{file="/var/lib/node_exporter/textfile_collector/my_random_number.prom"} 1.665064408e+09

想定通り出力されている。
毎分定期実行されるようcronに設定する。

# crontab -e
* * * * * /root/script/output_random.sh

暫く待ってからGrafanaのダッシュボード作成画面で「my_random_number」をクエリとしてグラフ作成すると、以下のようにmy_random_numberの値を可視化できる。

独自メトリクスをGrafanaで表示
独自メトリクスをGrafanaで表示

Pythonスクリプトで独自メトリクスの値を定期更新する

Prometheus公式のPython用Client(prometheus-client)がある。
Pythonプログラムに仕込んでメトリクス収集したりできるようだが、これを使ってTextfile Collector用のテキストファイルを作成できる。
こちらを参考に試してみる。

Pythonのインストール

# dnf module -y install python39

# python3 -V
Python 3.9.7

prometheus-clientのインストール

# pip3 install prometheus-client
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.
Collecting prometheus-client
  Downloading prometheus_client-0.14.1-py3-none-any.whl (59 kB)
     |????????????????????????????????| 59 kB 10.4 MB/s
Installing collected packages: prometheus-client
Successfully installed prometheus-client-0.14.1

シェルスクリプトの時と同様、ランダムな数値をmy_random_numberというメトリクス名で更新するスクリプトを作成する。

# vim output_random.py

output_random.py

#!/usr/bin/env python3

import random
from prometheus_client import CollectorRegistry, Gauge, write_to_textfile

FILE_PATH = '/var/lib/node_exporter/textfile_collector/my_random_number_py.prom'
METRICS_NAME="my_random_number_py"
METRICS_HELP="My random number by python script"

registry = CollectorRegistry()
g = Gauge(METRICS_NAME, METRICS_HELP, registry=registry)
g.set(random.randint(0, 10000))
write_to_textfile(FILE_PATH, registry)
  • Gauge()の第1引数はメトリック名、第2引数はヘルプ文字列。
  • コマンド、シェルスクリプトでファイル更新する際は、一時ファイルを経由することでアトミックなファイル更新を行っていた。
    prometheus-clientでは、write_to_textfile()関数がこの処理を行ってくれている。
    ここにあるwrite_to_textfile()のソースを見ると、一時ファイルを経由して更新する処理になっていることが分かる。
実行権限を付与
# chmod a+x /root/script/output_random.py

実行
# /root/script/output_random.py

出力を確認
# cat /var/lib/node_exporter/textfile_collector/my_random_number_py.prom
# HELP my_random_number_py My random number by python script
# TYPE my_random_number_py gauge
my_random_number_py 5277.0

メトリクスとして収集されていることを確認
# curl -s http://localhost:9100/metrics | grep my_random_number_py
# HELP my_random_number_py My random number by python script
# TYPE my_random_number_py gauge
my_random_number_py 1915
node_textfile_mtime_seconds{file="/var/lib/node_exporter/textfile_collector/my_random_number_py.prom"} 1.665068941e+09

想定通り収集されている。
あとはシェルスクリプトの時と同様に、定期実行してやればよい。
prometheus-clientを使うと、出力フォーマットを適切に整形してアトミックにテキストファイルを作成・更新してくれることが分かった。

まとめ

  • Node ExporterのTextfile Collectorで、簡単に独自メトリクスを収集できた。
  • Textfile Collector用のファイル更新は、シェルスクリプトでもPythonスクリプトでも比較的簡単なコードで実現できる。
    環境や独自メトリクス収集の処理内容に応じて選択すればよい。

サーバーのメトリクスを可視化する(Prometheus、Grafana、Node Exporter、cAdvisor)

サーバーのメトリクスを可視化して確認できるようにしたい。
PrometheusとGrafanaでやってみる。

環境

  • OS:RockyLinux8.6
  • Prometheus:v2.37.1
  • Grafana:9.1.6
  • Node Exporter:1.4.0
  • cAdvisor:0.45.0

構成図

今回構築する環境の構成イメージは以下の通り。

構成図
構成図

  • 出来るだけお手軽かつ、他の環境にもすぐに再現できる構成にしたいので、Prometheus、GrafanaはDockerコンテナで構成する。
  • ハードウェア・OSの標準的なメトリクスの取得は、Prometheusの代表的なexporterであるNode Exporterで収集する。
  • コンテナもよく使うので、コンテナのメトリクスも収集したい。cAdvisorで収集する。

※使用するソフトウェア・ツール一覧 prometheus.io

grafana.com

github.com

github.com

モニタリングサーバを構築する

DockerとDocker Composeインストール

こちらを参照し、Dockerをインストールする

# dnf remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

# dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# dnf -y install docker-ce docker-ce-cli containerd.io

# systemctl start docker

# docker info | grep Version
 Server Version: 20.10.18
 Cgroup Version: 1
 Kernel Version: 5.18.9-1.el8.elrepo.x86_64

# systemctl enable docker

# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:62af9efd515a25f84961b70f973a798d2eca956b1b2b026d0a4a63a3b0b6a3f2
Status: Downloaded newer image for hello-world:latest

Hello from Docker!

こちらを参照し、Docker Composeをインストールする

# dnf -y update

# dnf -y install docker-compose-plugin

# docker compose version
Docker Compose version v2.10.2

PrometeusとGrafanaを起動する

PrometheusとGrafanaをコンテナで起動する方法はそれぞれ以下に記載されている。

prometheus.io

grafana.com

今回はコンテナをDocker Composeで管理したいので、上記ページを参考にcomposeファイルを書いてみる。
composeファイルの書式は公式ドキュメントに従う。

composeファイル配置用ディレクトリを作成する(どこでもよい)
# mkdir -p /opt/docker/monitoring

composeファイルを作成する
# vim /opt/docker/monitoring/docker-compose.yml
services:
  prometheus:
    image: prom/prometheus:v2.37.1
    container_name: prometheus-2.37.1
    ports:
      - "9090:9090"
    volumes:
      - /opt/docker/monitoring/prometheus/etc:/etc/prometheus  # prometheusの設定ファイル置き場
      - /opt/docker/monitoring/prometheus/data:/prometheus  # prometheusのデータ保管場所
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=31d'  # ローカルストレージへのメトリクス保存期間
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'
  grafana:
    image: grafana/grafana-oss:9.1.6
    container_name: grafana-oss-9.1.6
    ports:
      - "3000:3000"
    volumes:
      - /opt/docker/monitoring/grafana/data:/var/lib/grafana  # Grafanaのデータ保管場所

→composeファイル記載時のポイントは以下。

  • ローカルストレージへのメトリクス保存期間はデフォルトだと15日間。短いので、今回は31日間に変更してみた。
    Prometheusのストレージに関するオプションはこちらを参照。
  • コンテナ内のデータはコンテナ終了時に消えてしまうため、永続化したいファイルの配置ディレクトリは、volumesでホストのストレージをマウントしている。
  • Prometheusのデータ保管場所は「/prometheus」だが、公式ドキュメントにはこの記載が見当たらなかった。
    最終的にprom/prometheusイメージのDockerfileを確認して特定した。

次に、Prometheusコンテナのvolumes用ディレクトリを作成する。
ディレクトリの所有ユーザ/グループはコンテナの実行ユーザ/グループと揃える。

# mkdir -p /opt/docker/monitoring/prometheus/etc

# mkdir /opt/docker/monitoring/prometheus/data

# chown 65534:65534 /opt/docker/monitoring/prometheus/*

# ls -l /opt/docker/monitoring/prometheus
合計 4
drwxr-xr-x 8 nobody nobody 4096 10月  4 08:19 data
drwxr-xr-x 3 nobody nobody   70 10月  3 15:39 etc

※所有ユーザ/グループの設定が誤っている場合、以下のエラーとなりコンテナの起動に失敗する。

# docker logs prometheus-2.37.1
(略)
ts=2022-10-04T07:51:22.656Z caller=query_logger.go:91 level=error component=activeQueryTracker msg="Error opening query log file" file=/prometheus/queries.active err="open /prometheus/queries.active: permission denied"
panic: Unable to create mmap-ed active query log

Prometheusコンテナの実行ユーザ/グループについてはドキュメントに記載がなかったので、以下のように確認した。 UID、GID共に65534だと分かる。

# docker run -it --rm --entrypoint sh prom/prometheus:v2.37.1
/prometheus $ id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)

Grafanaコンテナのvolumes用ディレクトリも同様に作成し、所有ユーザ/グループを設定する。

# mkdir -p /opt/docker/monitoring/grafana/data

# chown 472:0 /opt/docker/monitoring/grafana/data

# ls -l /opt/docker/monitoring/grafana
合計 0
drwxr-xr-x 2 472 root 6 10月  4 07:51 data

※所有ユーザ/グループの設定が誤っている場合、以下のエラーとなりコンテナの起動に失敗する。

# docker logs grafana-oss-9.1.6
GF_PATHS_DATA='/var/lib/grafana' is not writable.
You may have issues with file permissions, more information here: http://docs.grafana.org/installation/docker/#migrate-to-v51-or-later
mkdir: can't create directory '/var/lib/grafana/plugins': Permission denied

Grafanaコンテナの実行ユーザ/グループについてはGrafanaドキュメントのここによると、UIDは472、GIDは0。
実際のコンテナでも念のため確認した。ドキュメント通り。

# docker run -it --rm --entrypoint sh grafana/grafana-oss:9.1.6
/usr/share/grafana $ id
uid=472(grafana) gid=0(root) groups=0(root)

Prometheusの設定ファイル(prometheus.yml)を作成する。
内容は公式ドキュメントの「GETTING STARTED」を参考にした。

# vim /opt/docker/monitoring/prometheus/prometheus.yml
global:
  scrape_interval: 15s # Prometheusがtargetからメトリクスを取得する間隔
  external_labels:
    monitor: 'codelab-monitor'

scrape_configs:
  # Prometheus
  - job_name: 'prometheus'
    scrape_interval: 5s # global.scrape_intervalを上書き
    static_configs:
      - targets: ['localhost:9090']

Prometheusコンテナ、Grafanaコンテナを起動する。

# docker compose -f /opt/docker/monitoring/docker-compose.yml -p monitoring up -d

# docker ps
CONTAINER ID   IMAGE                       COMMAND                  CREATED              STATUS              PORTS                                       NAMES
e70494ddaeb8   grafana/grafana-oss:9.1.6   "/run.sh"                About a minute ago   Up About a minute   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   grafana-oss-9.1.6
23583f1fe8ce   prom/prometheus:v2.37.1     "/bin/prometheus --c…"   About a minute ago   Up About a minute   0.0.0.0:9090->9090/tcp, :::9090->9090/tcp   prometheus-2.37.1

動作確認と初期設定

ブラウザで「http://{モニタリングサーバのIP}:9090」に接続し、Prometheusの画面が表示されることを確認する。

Prometheus画面
Prometheus画面

Prometheusが収集したメトリクスをグラフ化して参照してみる。
prometheus.ymlでPrometheus自身のメトリクス収集を設定してあるので、試しにPrometheusのローカルストレージのチャンクの 1 秒あたりのレートをグラフ化してみる。
Graphタブを選択して、「rate(prometheus_tsdb_head_chunks_created_total[1m])」と入力してexecuteをクリックすると、以下のようにグラフが表示される。

Prometheusグラフ作成
Prometheusグラフ作成

Prometheusが正常に動作していることが確認できたので、次にGrafanaでPrometheusが収集したメトリクスをグラフ化してみる。

ブラウザで「http://{モニタリングサーバのIP}:3000」に接続し、Grafanaのログイン画面が表示されることを確認する。

Grafanaのログイン画面
Grafanaのログイン画面

username、password共に「admin」と入力して「Log in」をクリックし、ログインできることを確認する。

Grafanaログイン後の画面
Grafanaログイン後の画面

Prometheusが収集したメトリクスにアクセスするには、Grafanaにデータソースとして追加する必要がある。
Grafanaで「Configuration(画面左の歯車マーク)」→「Data sources」→「Add data source」→「Prometheus」と選択する。

Prometheusをデータソースに追加1
Prometheusをデータソースに追加1

上記のように、設定値の入力画面になる。設定値の詳細は以下を参照のこと。 Prometheus data source | Grafana documentation

URLに「http://prometheus-2.37.1:9090」と入力し、画面下部の「Save & test」をクリックする。
以下のように、「Data source is working」と表示されれば、正常にデータソースに追加出来ている。

Prometheusをデータソースに追加2
Prometheusをデータソースに追加2

データソースに追加できたので、試しにグラフ化してみる。
「Dashboards(画面左の四角マーク)」→「+ New dashboard」→「Add a new panel」と進む。
画面下のMetrics browserに、先ほどPrometheusでグラフ表示した際と同様、「rate(prometheus_tsdb_head_chunks_created_total[1m])」と入力して「Run queris」をクリックする。 以下のようにグラフが表示される。

Grafanaでのグラフ作成
Grafanaでのグラフ作成

モニタリングサーバにて、Prometheusでメトリクス収集し、Grafanaでそれをグラフ化できることを確認できた。
次は管理対象のサーバからメトリクス収集する設定を行う。

Node Exporterでハードウェア・OSのメトリクスを収集する

以下を参考に、Node Exporterを使って管理対象サーバのハードウェア・OSメトリクスを収集してみる。
prometheus.io

GitHub - prometheus/node_exporter: Exporter for machine metrics

Node Exporterのインストール・起動

管理対象サーバにtarballでNode Exporterをインストールする。
tarballはここからダウンロードできる。

# mkdir /opt/prometheus

# cd /opt/prometheus

# wget https://github.com/prometheus/node_exporter/releases/download/v1.4.0/node_exporter-1.4.0.linux-amd64.tar.gz

# tar zxf node_exporter-1.4.0.linux-amd64.tar.gz

# ls -l node_exporter-1.4.0.linux-amd64
合計 19200
-rw-r--r--. 1 3434 3434    11357  9月 26 12:39 LICENSE
-rw-r--r--. 1 3434 3434      463  9月 26 12:39 NOTICE
-rwxr-xr-x. 1 3434 3434 19640886  9月 26 12:33 node_exporter

# ln -s /opt/prometheus/node_exporter-1.4.0.linux-amd64/node_exporter /usr/local/bin/node_exporter

# ls -l /usr/local/bin/node_exporter
lrwxrwxrwx. 1 root root 61 10月 11 07:08 /usr/local/bin/node_exporter -> /opt/prometheus/node_exporter-1.4.0.linux-amd64/node_exporter

# rm -rf node_exporter-1.4.0.linux-amd64.tar.gz

Node Exporterを起動する。

# nohup node_exporter &

# ps aux | grep node_exporter | grep -v grep
root       14381  0.0  1.2 724844 22596 pts/0    Sl   11:53   0:00 ./node_exporter

以下のように、Node Exporterが公開しているメトリクスを「http://localhost:9100/metrics」で確認できる。

# curl -s http://localhost:9100/metrics  | head
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 3.1201e-05
go_gc_duration_seconds{quantile="0.25"} 6.0298e-05
go_gc_duration_seconds{quantile="0.5"} 8.5714e-05
go_gc_duration_seconds{quantile="0.75"} 0.000121806
go_gc_duration_seconds{quantile="1"} 0.003530947
go_gc_duration_seconds_sum 2.909751902
go_gc_duration_seconds_count 10781
# HELP go_goroutines Number of goroutines that currently exist.

Prometheusの設定変更

Node Exporterからメトリクスを収集するよう、Prometheusに設定をする。
モニタリングサーバのprometheus.ymlに以下のように追記をする。

# vim /opt/docker/monitoring/prometheus/etc/prometheus.yml
global:
  scrape_interval: 15s # Prometheusがtargetからメトリクスを取得する間隔
  external_labels:
    monitor: 'codelab-monitor'

scrape_configs:
  # Prometheus
  - job_name: 'prometheus'
    scrape_interval: 5s # global.scrape_intervalを上書き
    static_configs:
      - targets: ['localhost:9090']
  # Node Exporter
  - job_name: node
    file_sd_configs:
      - refresh_interval: 30s # targetsファイルの読み込み間隔
        files:                # targetsファイルを記載
        - /etc/prometheus/sd_files/node_targets.yml

# vim /opt/docker/monitoring/prometheus/etc/sd_files/node_targets.yml
- targets:

→メトリクス収集対象は、「job_name: 'prometheus'」に設定しているように「static_configs」に固定的に記載すると、設定変更時にPrometheusの再起動orリロードが必要になる。
 Prometheusには他にファイルベースのServiceDiscovery機能がある。
 「file_sd_configs」に設定したtargetsファイルに収集対象を記載することで、定期的(デフォルトは5分間隔)にファイルが読み込まれ、記載内容が自動反映される挙動となるため、これを使ってみる。
 ちなみに、今回は設定変更がすぐ反映されるよう、refresh_intervalを設定することで読み込み間隔を30秒に短縮している。
 ※targetsファイルの書式や、refresh_intervalについてはこちらを参照。

Prometheusをリロードして、prometheus.ymlの変更を反映する。
(リロード方法はこれを参考にした。)

# docker exec -it prometheus-2.37.1 killall -HUP prometheus

# docker logs prometheus-2.37.1
(略)
ts=2022-10-01T12:38:36.093Z caller=main.go:1177 level=info msg="Loading configuration file" filename=/etc/prometheus/prometheus.yml
ts=2022-10-01T12:38:36.093Z caller=main.go:1214 level=info msg="Completed loading of configuration file" filename=/etc/prometheus/prometheus.yml totalDuration=490.179μs db_storage=1.123μs remote_storage=1.402μs web_handler=592ns query_engine=1.256μs scrape=81.433μs scrape_sd=40.833μs notify=1.098μs notify_sd=1.645μs rules=1.487μs tracing=3.89μs

PrometheusのWeb画面で「Status」→「Targets」と選択すると、以下のように収集対象を確認できる。
node_targets.ymlに収集対象の情報を記載していないため、Node Exporterはまだ収集対象になっていない。

Targetsにはprometheus自身のみ表示されている
Targetsにはprometheus自身のみ表示されている

targetsファイルを編集する。JSONYAMLで記載できるが今回はYAMLで記載した。

# vim /opt/docker/monitoring/prometheus/etc/sd_files/node_targets.yml
- targets:
    - {管理対象サーバのIP}:9100
  labels:
    service: node # Node Exporterだと分かるように、Labelを付けておく。

編集後30秒以内には、ファイルベースのServiceDiscovery機能により管理対象サーバのNode ExporterがPrometheusのTargetsに追加される。

TargetsにNode Exporterが追加される
TargetsにNode Exporterが追加される

Grafanaのダッシュボードを作成

Grafanaでダッシュボードを作成するが、一から作成するのは手間がかかる。
Grafanaにはダッシュボードをインポートする機能があるので、インポートして作成する。

ダッシュボードは、以下サイトで公開されている。
インポートしたいダッシュボードのIDを控えておく。
grafana.com

今回は「Node Exporter Full」というダッシュボードを使わせていただく。

GrafanaのWeb画面で「Dashboards(画面左の四角マーク)」→「+ Import」と進み、「Import via grafana.com」に控えていたダッシュボードのIDを入力し、「Load」をクリックする。
以下のようにダッシュボードの情報が表示されるので、「Prometheus」にDataSourceに追加したPrometheusを選択し、「Import」をクリックする。

ダッシュボードのImport
ダッシュボードのImport

以下のように、インポートしたダッシュボードが表示される。
あとは必要に応じてカスタマイズしていけばOK。

Node Exporter Full
Node Exporter Full

Node Exporterの自動起動設定

Node Exporterをtarballでインストールした場合、そのままだと自動起動設定できないので、systemd service化する必要がある。
以下を参考にやってみる。

node_exporter/examples/systemd at master · prometheus/node_exporter · GitHub

Installing node_exporter as systemd serivice · GitHub

Node Exporter用のユーザを作成し、必要な権限設定・ディレクトリ作成を行う。

Node Exporter実行用のユーザを作成
# useradd --system --shell /sbin/nologin node_exporter

ユーザが作成されたことを確認
# cat /etc/group | grep node_exporter
node_exporter:x:985:

Node Exporterの実行ファイルの権限を変更
# chown node_exporter:node_exporter /usr/local/bin/node_exporter

リンク先を確認(シンボリックリンク自体のパーミッションに意味はないので気にしなくてよい。)
# ls -l /usr/local/bin/node_exporter
lrwxrwxrwx. 1 root root 61 10月 11 07:08 /usr/local/bin/node_exporter -> /opt/prometheus/node_exporter-1.4.0.linux-amd64/node_exporter

Node Exporterの実行ファイルの権限を確認
# ls -l /opt/prometheus/node_exporter-1.4.0.linux-amd64/node_exporter
-rwxr-xr-x. 1 node_exporter node_exporter 19640886  9月 26 12:33 /opt/prometheus/node_exporter-1.4.0.linux-amd64/node_exporter

Node ExporterのTEXTFILE COLLECTOR用ディレクトリを作成
# mkdir -p /var/lib/node_exporter/textfile_collector

TEXTFILE COLLECTOR用ディレクトリの権限設定
# chown node_exporter:node_exporter /var/lib/node_exporter/textfile_collector

TEXTFILE COLLECTOR用ディレクトリの権限確認
# ll /var/lib/node_exporter
合計 0
drwxr-xr-x. 2 node_exporter node_exporter 6 10月  2 14:54 textfile_collector

serviceの定義ファイルを作成する。

# cat <<EOF > /etc/systemd/system/node_exporter.service
[Unit]
Description=Node Exporter

[Service]
User=node_exporter
EnvironmentFile=-/etc/sysconfig/node_exporter
ExecStart=/usr/local/bin/node_exporter \$OPTIONS

[Install]
WantedBy=multi-user.target
EOF

# echo "OPTIONS=\"--collector.textfile.directory /var/lib/node_exporter/textfile_collector\"" > /etc/sysconfig/node_exporter

serviceとして登録する。

# systemctl daemon-reload

# systemctl enable --now node_exporter
Created symlink /etc/systemd/system/multi-user.target.wants/node_exporter.service → /etc/systemd/system/node_exporter.service.

node_exporterが正常に起動していることを確認する。「Active: active (running)」となっていればOK。
これで、サーバを再起動しても自動的にnode_exporterが起動する。

# systemctl status node_exporter
● node_exporter.service - Node Exporter
   Loaded: loaded (/etc/systemd/system/node_exporter.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2022-10-11 07:18:07 UTC; 33s ago
 Main PID: 421117 (node_exporter)
    Tasks: 5 (limit: 10992)
   Memory: 8.0M
   CGroup: /system.slice/node_exporter.service
           mq421117 /usr/local/bin/node_exporter --collector.textfile.directory /var/lib/node_exporter/textfile_collector

10月 11 07:18:07 ip-10-0-102-254.ap-northeast-1.compute.internalmydomain.local node_exporter[421117]: ts=2022-10-11T07:18:07.479Z caller=node_exporter.go:115 level=info collector=thermal_zone
10月 11 07:18:07 ip-10-0-102-254.ap-northeast-1.compute.internalmydomain.local node_exporter[421117]: ts=2022-10-11T07:18:07.479Z caller=node_exporter.go:115 level=info collector=time
10月 11 07:18:07 ip-10-0-102-254.ap-northeast-1.compute.internalmydomain.local node_exporter[421117]: ts=2022-10-11T07:18:07.479Z caller=node_exporter.go:115 level=info collector=timex
10月 11 07:18:07 ip-10-0-102-254.ap-northeast-1.compute.internalmydomain.local node_exporter[421117]: ts=2022-10-11T07:18:07.479Z caller=node_exporter.go:115 level=info collector=udp_queues
10月 11 07:18:07 ip-10-0-102-254.ap-northeast-1.compute.internalmydomain.local node_exporter[421117]: ts=2022-10-11T07:18:07.479Z caller=node_exporter.go:115 level=info collector=uname
10月 11 07:18:07 ip-10-0-102-254.ap-northeast-1.compute.internalmydomain.local node_exporter[421117]: ts=2022-10-11T07:18:07.479Z caller=node_exporter.go:115 level=info collector=vmstat
10月 11 07:18:07 ip-10-0-102-254.ap-northeast-1.compute.internalmydomain.local node_exporter[421117]: ts=2022-10-11T07:18:07.479Z caller=node_exporter.go:115 level=info collector=xfs
10月 11 07:18:07 ip-10-0-102-254.ap-northeast-1.compute.internalmydomain.local node_exporter[421117]: ts=2022-10-11T07:18:07.479Z caller=node_exporter.go:115 level=info collector=zfs
10月 11 07:18:07 ip-10-0-102-254.ap-northeast-1.compute.internalmydomain.local node_exporter[421117]: ts=2022-10-11T07:18:07.479Z caller=node_exporter.go:199 level=info msg="Listening on" address=:9100
10月 11 07:18:07 ip-10-0-102-254.ap-northeast-1.compute.internalmydomain.local node_exporter[421117]: ts=2022-10-11T07:18:07.479Z caller=tls_config.go:195 level=info msg="TLS is disabled." http2=false

Node Exporterの収集対象を制御する

Node Exporterの収集対象は、以下のオプションで制御できる。
「<name>」に指定する文字列はこちらの表で確認できる。

  • デフォルトで無効な収集対象を有効にする:--collector.<name>
  • デフォルトで有効な収集対象を無効にする:--no-collector.<name>
  • 特定の収集対象のみ有効にする:--collector.disable-defaults --collector.<name>

実際に設定変更して確認してみる。

デフォルトで収集されているメトリクスの数を確認。

# curl -s http://localhost:9100/metrics | grep -v "#" | wc -l
568

デフォルトで有効な収集対象を全て無効にすると、メトリクス数は減っているが0にはならなかった。
無効化できない最低限のメトリクスがある模様。

# vim /etc/sysconfig/node_exporter
OPTIONS="--collector.textfile.directory /var/lib/node_exporter/textfile_collector --collector.disable-defaults"

# systemctl restart node_exporter

# curl -s http://localhost:9100/metrics | grep -v "#" | wc -l
47

# curl -s http://localhost:9100/metrics | grep -v "#" | cut -f 1 -d " " > /tmp/disable-defaults.txt

cpuとmeminfoだけ有効化してみると、メトリクス数がやや増えた。

# vim /etc/sysconfig/node_exporter
OPTIONS="--collector.textfile.directory /var/lib/node_exporter/textfile_collector --collector.disable-defaults --collector.cpu --collector.meminfo"

# systemctl restart node_exporter

# curl -s http://localhost:9100/metrics | grep -v "#" | wc -l
122

メトリクス項目の差分を見ると、CPU、メモリのメトリクスが増えていることが分かる。

# diff /tmp/disable-defaults.txt /tmp/cpu_meminfo.txt
(略)
> node_cpu_guest_seconds_total{cpu="0",mode="nice"}
> node_cpu_guest_seconds_total{cpu="0",mode="user"}
(略)
> node_memory_WritebackTmp_bytes
> node_memory_Writeback_bytes
> node_scrape_collector_duration_seconds{collector="cpu"}
> node_scrape_collector_duration_seconds{collector="meminfo"}
> node_scrape_collector_success{collector="cpu"}
> node_scrape_collector_success{collector="meminfo"}

cAdvisorでDockerコンテナのメトリクスを収集する

以下を参考に、cAdvisorを使って管理対象サーバのコンテナのメトリクスを収集してみる。

Monitoring Docker container metrics using cAdvisor | Prometheus

GitHub - google/cadvisor: Analyzes resource usage and performance characteristics of running containers.

cAdvisorの起動

管理対象サーバでcAdvisorコンテナを起動する。
Advisorコンテナは、サーバ再起動後も自動起動してほしいので、上記リンクの起動コマンドに「--restart=always」を追加した。

# VERSION=v0.45.0
# docker run \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:ro \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --volume=/dev/disk/:/dev/disk:ro \
  --publish=8080:8080 \
  --detach=true \
  --name=cadvisor \
  --privileged \
  --device=/dev/kmsg \
  --restart=always \
  gcr.io/cadvisor/cadvisor:$VERSION

# docker ps
CONTAINER ID   IMAGE                              COMMAND                  CREATED         STATUS                            PORTS                                       NAMES
f36db3b97eee   gcr.io/cadvisor/cadvisor:v0.45.0   "/usr/bin/cadvisor -…"   5 seconds ago   Up 4 seconds (health: starting)   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   cadvisor

ブラウザで「http://{管理対象サーバのIP}:8080/」に接続すると、cAdvisorのWeb画面を参照できる。

cAdvisorの画面
cAdvisorの画面

また、cAdvisorが公開しているメトリクスを「http://localhost:8080/metrics」で確認できる。

# curl -s http://localhost:9100/metrics  | head
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 3.1201e-05
go_gc_duration_seconds{quantile="0.25"} 6.0298e-05
go_gc_duration_seconds{quantile="0.5"} 8.5714e-05
go_gc_duration_seconds{quantile="0.75"} 0.000121806
go_gc_duration_seconds{quantile="1"} 0.003530947
go_gc_duration_seconds_sum 2.909751902
go_gc_duration_seconds_count 10781
# HELP go_goroutines Number of goroutines that currently exist.

各メトリクスの説明は以下を参照。
cadvisor/prometheus.md at master · google/cadvisor · GitHub

※ちなみに、以下のようにcAdvisorの実行オプションに--enable_metricsを追加することで、デフォルトで有効になっていない収集メトリクスを有効化できる(無効にしたい場合は--disable_metrics)。
--enable_metricsに指定する文字列は、上記メトリクス説明ページの表を参照。カンマ区切りで複数指定できる。

# VERSION=v0.45.0
# docker run \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:ro \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --volume=/dev/disk/:/dev/disk:ro \
  --publish=8080:8080 \
  --detach=true \
  --name=cadvisor \
  --privileged \
  --device=/dev/kmsg \
  --restart=always \
  gcr.io/cadvisor/cadvisor:$VERSION \
  --enable_metrics=cpu_topology,memory

確認用コンテナの起動

確認用に、Redisコンテナを起動する。

# docker run --publish=6379:6379 --detach=true --name=redis redis:latest

# docker ps
CONTAINER ID   IMAGE                              COMMAND                  CREATED         STATUS                   PORTS                                       NAMES
8b5881b568b2   redis:latest                       "docker-entrypoint.s…"   2 seconds ago   Up 1 second              0.0.0.0:6379->6379/tcp, :::6379->6379/tcp   redis
f36db3b97eee   gcr.io/cadvisor/cadvisor:v0.45.0   "/usr/bin/cadvisor -…"   7 minutes ago   Up 7 minutes (healthy)   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   cadvisor

cAdvisorはサーバ内に存在しているコンテナ単位にメトリクスを収集する。
ブラウザで「http://{管理対象サーバのIP}:8080/docker/redis」に接続すると、redisコンテナのメトリクスが見られる。

redisコンテナのメトリクス
redisコンテナのメトリクス

Prometheusの設定変更

cAdvisorからメトリクスを収集するよう、Prometheusに設定をする。
モニタリングサーバのprometheus.ymlに以下のように追記をする。

# vim /opt/docker/monitoring/prometheus/etc/prometheus.yml
global:
  scrape_interval: 15s # Prometheusがtargetからメトリクスを取得する間隔
  external_labels:
    monitor: 'codelab-monitor'

scrape_configs:
  # Prometheus
  - job_name: 'prometheus'
    scrape_interval: 5s # global.scrape_intervalを上書き
    static_configs:
      - targets: ['localhost:9090']
  # Node Exporter
  - job_name: node
    file_sd_configs:
      - refresh_interval: 30s # targetsファイルの読み込み間隔
        files:                # targetsファイルを記載
        - /etc/prometheus/sd_files/node_targets.yml
  # cAdvisor
  - job_name: cadvisor
    scrape_interval: 5s
    file_sd_configs:
      - refresh_interval: 30s # targetsファイルの読み込み間隔
        files:                # targetsファイルを記載
        - /etc/prometheus/sd_files/cadvisor_targets.yml

# vim /opt/docker/monitoring/prometheus/etc/sd_files/cadvisor_targets.yml
- targets:
    - {管理対象サーバのIP}:8080
  labels:
    service: cadvisor

Prometheusをリロードして、prometheus.ymlの変更を反映する。

# docker exec -it prometheus-2.37.1 killall -HUP prometheus

少し待ってからPrometheusのWeb画面で「Status」→「Targets」を確認すると、以下のようにcAdvisorが追加されている。

TargetsにcAdvisorが追加される
TargetsにcAdvisorが追加される

Grafanaのダッシュボードを作成

Node Exporter同様、インポートしてダッシュボードを作成する。 「Docker Container」というダッシュボードを使わせていただく。

インポートすると、以下のようにコンテナのメトリクスが表示される。

Docker Container
Docker Container

補足:cAdvisorの警告メッセージ

cAdvisorコンテナから、「There are no NVM devices!」というメッセージが出力され続けていることに気付いた。

# docker logs cadvisor
I1005 07:08:45.103690       1 manager.go:1191] Exiting thread watching subcontainers
I1005 07:08:45.103714       1 manager.go:405] Exiting global housekeeping thread
I1005 07:08:45.104268       1 cadvisor.go:210] Exiting given signal: terminated
W1005 07:10:20.790347       1 machine_libipmctl.go:64] There are no NVM devices!
W1005 07:15:20.942362       1 machine_libipmctl.go:64] There are no NVM devices!

→Warningだし、特に問題無く動作しているように思える。できれば消したいが、消す方法が見つからなかった。
NVMデバイスがないサーバでは出力させ続けるしかないのかもしれない。

まとめ

  • Docker ComposeでPrometheusとGrafanaを起動することで、簡単にモニタリング環境を構築できた。
  • Node ExporterとcAdvisorを使用することで、ハードウェア・OS・コンテナのメトリクスを収集出来ることを確認した。
  • 今後は以下も試してみたい。
    • Promeheusuのリモートストレージの利用
    • Node Exporterで独自メトリクスを収集 →やってみた。記事はこちら
    • Prometheusの他のServiceDiscovery方法を試す
    • Prometheusのリラベル機能を試す
    • ログ収集も統合出来ないか試す

NetdataをRockyLinux8にインストールする

Netdataというサーバのメトリクスを取得・可視化するOSSをRockyLinux8にインストールした時のメモ。

環境

・OS:Rocky Linux 8.6
・Netdata:v1.36.1

実施内容

インストール手順は以下に記載されている。
learn.netdata.cloud

デフォルトの挙動だと、リポジトリを追加して、dnfでNetdataのパッケージがインストールされる。
この際に依存関係にあるパッケージ(Python3.8など)も一緒にインストールされるが、これが不都合な場合がある。
依存ライブラリが含まれた静的バイナリを配置する方法であれば余計なパッケージがインストールされないので、今回はその方法でインストールする。

インストールは以下のように実施した。 インストールスクリプト実行時に付けているオプションの意味は以下。
--no-updates:Netdataの自動更新を無効にする
--install-version {バージョン番号}:インストールするNetdataのバージョンを指定する(安定最新版をインストールしたい場合は「--stable-channel」に変える)
--disable-telemetry:統計送信をさせない
--non-interactive:インストール中にyes/noを聞かれなくなる(全てyesと答えたことになる)
--static-only:静的バイナリを配置する方法でインストールを行う

インストールスクリプトを実行
# wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh && sh /tmp/netd
ata-kickstart.sh --no-updates --install-version 1.36.1--disable-telemetry --static-only --non-interactive

netdataが正常に起動していることを確認
# systemctl status netdata
● netdata.service - Real time performance monitoring
   Loaded: loaded (/usr/lib/systemd/system/netdata.service; enabled; vendor preset: disabled)
   Active: active (running) since Sat 2022-10-01 13:56:34 UTC; 1min 27s ago
  Process: 1805 ExecStartPre=/bin/chown -R netdata:netdata /opt/netdata/var/run/netdata (code=exited, status=0/SUCCESS)
  Process: 1802 ExecStartPre=/bin/mkdir -p /opt/netdata/var/run/netdata (code=exited, status=0/SUCCESS)
  Process: 1800 ExecStartPre=/bin/chown -R netdata:netdata /opt/netdata/var/cache/netdata (code=exited, status=0/SUCCESS)
  Process: 1799 ExecStartPre=/bin/mkdir -p /opt/netdata/var/cache/netdata (code=exited, status=0/SUCCESS)
 Main PID: 1807 (netdata)
    Tasks: 58 (limit: 10992)
   Memory: 125.8M
   CGroup: /system.slice/netdata.service
           tq1807 /opt/netdata/bin/srv/netdata -P /opt/netdata/var/run/netdata/netdata.pid -D
           tq1810 /opt/netdata/bin/srv/netdata --special-spawn-server
           tq1945 /opt/netdata/usr/libexec/netdata/plugins.d/apps.plugin 1
           tq1975 /opt/netdata/usr/libexec/netdata/plugins.d/ebpf.plugin 1
           mq1978 /opt/netdata/usr/libexec/netdata/plugins.d/go.d.plugin 1

ブラウザで「http://{インストールしたサーバのIP}:19999」に接続すると、以下のようにNetdataの画面が表示される。
内容を見ると、様々なメトリクスのリアルタイムの情報が可視化されていることが分かる。

Netdataの画面
Netdataの画面