Web Server(IPv6)を監視するために、Fedora CoreOSにPodmanで監視コンテナ(Prometheus+Blackbox Exporter+Grafana)を作成してみました。
自宅サーバとNetlifyに構築したWeb Serverを外形監視したいと思います。外形監視はPrometheus+Blackbox Exporterで行います。Web Serverに対してIPv6でHTTPS通信してその応答時間を監視していきます。環境は自宅サーバのFedora CoreOSにPodmanでコンテナを構築していきます。
監視コンテナの環境
コンテナは自宅サーバのFedora CoreOS上にPodmanで構築していきます。またデプロイはgitLab.com(CI/CD)から自宅サーバでリモートシェルを使って行います。こちらの環境については別途備忘録を残していきたいと思います。監視コンテナは、backendポッドに作成していきたいと思います。
PodmanはFedora CoreOSをセットアップすると標準で入っていますので新たにインストールする必要はありません。残念ながらPodman 3.0ではまだないよです。
Release Date: Mar 03, 2021:ignition2.9.0 kernel5.10.19 moby-engine19.03.13 podman3.0.1 rpm-ostree2021.2 systemd246.7
$ cat /etc/os-release
NAME=Fedora
VERSION="33.20210201.3.0 (CoreOS)"
ID=fedora
VERSION_ID=33
:
$ podman --version
podman version 2.2.1
今回は、以下のコンテナを構築していきます。
No | ID | 用途 | Dockerイメージ | ポート番号 [外部:内部] |
備考 |
---|---|---|---|---|---|
1 | backend | Pod | - | 52100-52110:52100-52110 | 後で利用するかもしれないので取り敢えず11Port外部と開けてます。 |
2 | pr2 | proxy | alpine + (nginx) | 52100:52100 | nginxのDockeイメージではなくAlpineに個別インストール |
2 | prothemeus | イベント監視 | prom/prometheus | 52101:52101 | |
3 | balckbox-exporter | 外形監視 | prom/blackbox-exporter | 9115 | ポート番号9115はDefault値でPod内のみの通信、外形監視はhttps |
4 | grafana | 監視結果のグラフ | grafana/grafana | 52102:52102 |
backendポッドの作成
ポッドは、以下のコマンドで作成します。
podman pod create --name backend --net=slirp4netns:enable_ipv6=true -p 52100-52110:52100-52110
rootlessコンテナとしたいのでコンテナから外部のサーバとIPv6で通信するためにnetオプションでslirp4netns:enable_ipv6=trueを指定しています。ポッド内のコンテナ間通信はこの設定をしなくてもIPv6通信ができると思いますが、外との通信にはこの指定をすると良さそうです。なお外からコンテナとIPv6通信するためにはこの指定ではなく、rootfullでIPv6のアドレス指定するやり方でやるしかないと思います。おそらく。こちらの方法はコンテナ用サーバをFedora Core OSで構築してみました。のTip podmanでIPv6コンテナ通信(ULA)を参照してください。
この指定をしないでGrafanaでみるとStatusがDOWNとなっていました。試しにgoogle.comをデバッグモードでみてみるとip_protocol=ip6となっていましたがProbe failedとエラーを吐いていました。ip_protocol=ip4にしてIPv4で外形監視する方法もあるようですが私はコンテナをIPv6に対応して回避しました。
$ podman exec -it pr1 sh
curl 'localhost:9115/probe?target=google.com&debug=true' ← pr1(proxyコンテナ)からcurlでprobeしてみる。
Logs for the probe:
ts=2021-02-24T12:22:39.035707468Z caller=main.go:304 module=http_2xx target=google.com level=info msg="Beginning probe" probe=http timeout_seconds=119.5
ts=2021-02-24T12:22:39.03586488Z caller=http.go:342 module=http_2xx target=google.com level=info msg="Resolving target address" ip_protocol=ip6
ts=2021-02-24T12:22:39.037457859Z caller=http.go:342 module=http_2xx target=google.com level=info msg="Resolved target address" ip=2404:6800:4004:81b::200e
ts=2021-02-24T12:22:39.037551564Z caller=client.go:252 module=http_2xx target=google.com level=info msg="Making HTTP request" url=http://[2404:6800:4004:81b::200e] host=google.com
ts=2021-02-24T12:22:39.037716724Z caller=main.go:119 module=http_2xx target=google.com level=error msg="Error for HTTP request" err="Get \"http://[2404:6800:4004:81b::200e]\": dial tcp [2404:6800:4004:81b::200e]:80: connect: network is unreachable"
ts=2021-02-24T12:22:39.037760284Z caller=main.go:119 module=http_2xx target=google.com level=info msg="Response timings for roundtrip" roundtrip=0 start=2021-02-24T12:22:39.037636095Z dnsDone=2021-02-24T12:22:39.037636095Z connectDone=2021-02-24T12:22:39.037684075Z gotConn=0001-01-01T00:00:00Z responseStart=0001-01-01T00:00:00Z end=0001-01-01T00:00:00Z
ts=2021-02-24T12:22:39.037797644Z caller=main.go:304 module=http_2xx target=google.com level=error msg="Probe failed" duration_seconds=0.002054087
コンテナを外との通信にIPv6通信を許可する。
podman pod create --nameポット名--net=slirp4netns:enable_ipv6=true・・・
ポッドは、gitLab.com(CI/CD)からリモートシェルを使って自宅サーバでコンテナをデプロイします。自宅サーバにgitLab-runnerのコンテナをたて自宅サーバ側で実行するようにしています。
自宅Mac(VSC)-git→gitLab.com(CI/CD)-rshell→自宅サーバ(dev-pod:gitLab-runner)-rshell→自宅サーバ(ホストPodman:deploy)のような流れです。
用途別に.gitlab-ci-ymlを作成したいので、メインの.gitlab-ci-ymlは以下のように記述しました。
## v2.1 akibo.I
include:
- 'backend/.gitlab-ci.yml'
variables:
GIT_SSL_NO_VERIFY: '1'
stages:
- buildPod
- buildContainer
before_script:
- set -x
- pwd
after_script:
こちらが実際のポッド構築のスクリプトとなります。podmanを使ってshellでデプロイしています。
## v2.1 akibo.I
## backend
variables:
backend-pod:
stage: buildPod
only:
changes:
- backend/pod
script:
- HOST="xxxx@yyyy"
- POD="backend"
- ssh $HOST podman pod stop -i $POD
- ssh $HOST podman pod rm -i $POD
- ssh $HOST podman pod create --name $POD --net=slirp4netns:enable_ipv6=true -p 52100-52110:52100-52110
proxyコンテナの作成
自宅サーバは、別サーバでfrontEnd Serverが動作していますので、そちらから転送されたものを各コンテナに転送するproxyコンテナを作成します。
fronend-Pod(pr1)→backend-Pod(pr2,balckbox-exporter,prometheus,grafana)
フォルダ構成
backend
├──.gitlab-ci.yml
├──pr2
│ ├──Dockerfile
│ ├──build
│ ├──conf.d
│ │ ├──default.conf
│ │ ├──header.conf
│ │ └──log.conf
│ ├──nginx.conf
│ └──vhost.d
│ ├──01_ap-Server.conf
│ └──99_default.conf
Dockerfile
nginxの設定ファイルを作成しそのままコピーするようにしています。
FROM alpine
LABEL version="1.0" "akiboi.net"="akiboI"
RUN apk add --update --no-cache nginx
COPY build/ /etc/nginx/CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
nginxの起動は-gオプションでdaemon offとしています。
podmanのデプロイ
ホスト側でDockefileを元にビルドしますが、gitLab-runnerのコンテナ内にあるbuildsディレクトは動的に変わるのでこの対応をします。
gitlab-runnerコンテナのボリュームマップは -v /vol/gitlab-runner/data:/home/gitlab-runner:Zとしています。
Dockerファイルを例にするとコンテナ側は、/home/gitlab-runner/builds/動的フォルダ/0/・・・/backend/pr2/Dockerfileとなります。このファイルはホスト側では/vol/gitlab-runner/data/builds//動的フォルダ/0/・・・/backend/pr2/Dockerfileとなります。ホスト側のPodmanでビルドしていきますので、sedコマンドでコンテナ側のカレントディレクトを元に取得しています。以前はgitlab-runnerをホスト側で動かしていましたのでこのようなことをする必要はありませんでしたが、Fedora CoreOSに変更してからはgitlab-runner自身もコンテナに変更しましたのでこのような対応が必要でした。なおpr2コンテナのボリュームマップは、-v /vol/pr2/data:/usr/share/nginx:Zと -v /vol/pr2/log:/var/log/nginx:Zとしました。
pr1:
stage: buildContainer
only:
changes:
- backend/pr2/**/*
script:
- HOST="xxxx@yyyy"
- POD="backend"
- BASEDIR="/vol"
- HOMEDIR=`pwd` - WORKDIR=`echo ${HOMEDIR} | sed -e "s/^\/home\/gitlab-runner/\/vol\/gitlab-runner\/data/g"` - CONTAINER=pr2
- VOLUME="-v $BASEDIR/$CONTAINER/data:/usr/share/nginx:Z -v $BASEDIR/$CONTAINER/log:/var/log/nginx:Z "
- REGISTRY="registry.akiboi.net:5000"
- ssh $HOST mkdir -p $BASEDIR/$CONTAINER/data $BASEDIR/$CONTAINER/log
- ssh $HOST podman build -t $REGISTRY/$CONTAINER $WORKDIR/$POD/$CONTAINER - ssh $HOST podman push $REGISTRY/$CONTAINER
- ssh $HOST podman stop -i $CONTAINER
- ssh $HOST podman rm -i $CONTAINER
- ssh $HOST podman run -dt --pod $POD --restart=always --name $CONTAINER $VOLUME $REGISTRY/$CONTAINER
Balckbox ExporterとPrometheusコンテナの作成
Balckbox ExporterとPrometheusコンテナを作成していきます。
ビルド用フォルダ
backend
:
├──blackbox-exporter
│ └──Dockerfile
├──prometheus
│ ├──Dockerfile
│ └──build
│ └──prometheus.yml
Blackbox exporter Dockerfile
prom/blackbox-exporterイメージを利用します。設定ファイルはディフォルトのものをそのまま利用しますのでDockerfileのみでOKです。
FROM prom/blackbox-exporter
LABEL version="1.0" "akiboi.net"="akiboI"
prometheu Dockerfile
prom/prometheusイメージを利用しています。configファイルやポート番号はCMDで指定しています。
FROM prom/prometheus
LABEL version="1.0" "akiboi.net"="akiboI"
USER 0
COPY build/prometheus.yml /etc/prometheus
CMD [ "--config.file=/etc/prometheus/prometheus.yml","--web.listen-address=:52103","--web.console.libraries=/usr/share/prometheus/console_libraries","--web.console.templates=/usr/share/prometheus/consoles","--web.enable-admin-api"]
USER 0(root)を設定しています。この指定をしないとnobodyとなりホスト側のボリュームマッピングしたフォルダにアクセスできないためです。 rootユーザ名前空間にマッピングされていないUIDが所有するファイルオブジェクトは「nobody」(65534、kernel.overflowuid)が所有するものとして扱われます。
今回は、rootlessユーザID 1001で実行しています。この時の名前空間を見てみると以下のようになっています。グループIDも同様ですが、要はコンテナ側のユーザID 0(root)の場合は、ホスト側rootlessの実行ユーザーIDとマッピングされてます。このおかげで、ホスト側のrootlessユーザIDで作成したVoluemマッピングのフォルダにアクセスできるのだと思います。通常のDockerイメージはroot IDで作成されていることが多いので意識する必要ありませんが別IDで作成されている場合などは注意が必要と思います。
$ podman unshare id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:system_r:container_runtime_t:s0-s0:c0.c1023
$ podman unshare cat /proc/self/uid_map
0 1001 1
1 165536 65536
$ podman unshare cat /proc/self/gid_map
0 1001 1
1 165536 65536
prometheus.ymlの設定
prometheus.ymlは、監視するwebサーバを指定してその他はディフォルトのままとしました。テストのために監視サーバは、google.comとNetlify、自宅サーバとしてみました。
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets:
rule_files:
scrape_configs:
- job_name: 'blackbox_http'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://google.com/ - https://www.akiboi.net/ ← 自宅サーバ - https://www.akiboi.info/ ← Netlify relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: localhost:9115
Blackbox ExporterとPrometheusの.gitlab-ci.yml
proxyコンテナとほとんど同じで、ビルド判断するフォルダとコンテナ名、ボリュームマッピングが違うのみです。
blackbox-exporter:
stage: buildContainer
only:
changes: - backend/blackbox-exporter/**/*
script:
- HOST="xxxx@yyyy"
- POD="backend"
- BASEDIR="/vol"
- HOMEDIR=`pwd`
- WORKDIR=`echo ${HOMEDIR} | sed -e "s/^\/home\/gitlab-runner/\/vol\/gitlab-runner\/data/g"` - CONTAINER=blackbox-exporter - VOLUME=""
- REGISTRY="registry.akiboi.net:5000"
- ssh $HOST mkdir -p $BASEDIR/$CONTAINER/data
- ssh $HOST podman build -t $REGISTRY/$CONTAINER $WORKDIR/$POD/$CONTAINER
- ssh $HOST podman push $REGISTRY/$CONTAINER
- ssh $HOST podman stop -i $CONTAINER
- ssh $HOST podman rm -i $CONTAINER
- ssh $HOST podman run -dt --pod $POD --restart=always --name $CONTAINER $VOLUME $REGISTRY/$CONTAINER
prometheus:
stage: buildContainer
only:
changes: - backend/prometheus/**/*
script:
- HOST="xxxx@yyyy"
- POD="backend"
- BASEDIR="/vol"
- HOMEDIR=`pwd`
- WORKDIR=`echo ${HOMEDIR} | sed -e "s/^\/home\/gitlab-runner/\/vol\/gitlab-runner\/data/g"` - CONTAINER=prometheus - VOLUME="-v $BASEDIR/$CONTAINER/data:/prometheus/data:Z "
- REGISTRY="registry.akiboi.net:5000"
- ssh $HOST mkdir -p $BASEDIR/$CONTAINER/data
- ssh $HOST podman build -t $REGISTRY/$CONTAINER $WORKDIR/$POD/$CONTAINER
- ssh $HOST podman push $REGISTRY/$CONTAINER
- ssh $HOST podman stop -i $CONTAINER
- ssh $HOST podman rm -i $CONTAINER
- ssh $HOST podman run -dt --pod $POD --restart=always --name $CONTAINER $VOLUME $REGISTRY/$CONTAINER
Black ExporterとPrometheusの動作確認
PrometheusでWebサーバを監視できているか確認してみます。標準であれば http://prometheusサーバ:9090/でアクセスするとPrometheusの初期画面が表示できると思います。私の環境はFrontEnd ServerのProxy1の設定も行いhttps://●●●●.akiboi.net/でアクセスしてみます。ヘッダーメニューからStatus-Targetsを選ぶとBlack Exporterと接続されていることが確認できました。
メトリックスも確認していきます。ヘッダーメニューからGraphを選択します。probe_http_duration_secondsと入力してExecuteボタンをクリックするとメトリックスが表示されます。Graphに切り替えてみましょう。
自宅からNetlifyは流石に遠いのでDurationは400ms、Googleが200ms、自宅サーバは同じLAN上ですので20msといったぐらいでしょうか。次回はこれをGrafanaを使ってダッシュボードを作っていきたいと思います。