K3s をシングルノードとして起動してイングレスコントローラーとして Ingress Nginx を使い、接続元のIPアドレスをリクエストヘッダー (x-real-ip, x-forwarded-for) として取得する設定です。
ラズベリーパイや類するシングルボードコンピューターに K8s 環境を構築するのに良い方法となります。
課題としては
の2つがあり、今回のプラクティスで両方を解決します。
K3s は、デフォルトだとイングレスコントローラーとして Traefik をインストールします。
設定項目が Nginx と違うため、Nginx と同じ感覚で使っていると思い通りに動作しない可能性があります。
Ingress Nginx をインストールし、K3s のロードバランサー (デフォルトでは Service LB) から接続を受けるようにすると、Nginx で接続元の IP アドレスをリクエストヘッダーとして扱うことができません。 つまり、アプリ側で接続元IPアドレスをロギングしたり、アクセス制御に使うことができません。
関連課題:
Getting Real Client IP with k3s · k3s-io/k3s · Discussion #2997
今回は、Ingress Nginx を Deployment + Service ではなく、DaemonSet として起動して、ポートを HostPort の 80, 443 としてバインドすることで、ホストマシンへのパケットを直接 Ingress Nginx へ渡すようにします。 これは MicroK8s のデフォルトの Ingress Nginx と同じアプローチであり、適用後の Ingress の使用感は MicroK8s のようになります。
この場合、Ingress Nginx 自体を冗長化できないとか、権限的な課題はあります。 また、マルチノード運用には向きません。
結果として、MicroK8s をよりコンパクトにしたようなシングルノードクラスタが完成します。
$ cat /proc/cgroups
$ cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 0 88 1
cpu 0 88 1
cpuacct 0 88 1
blkio 0 88 1
memory 0 88 1
devices 0 88 1
freezer 0 88 1
net_cls 0 88 1
perf_event 0 88 1
net_prio 0 88 1
hugetlb 0 88 1
pids 0 88 1
rdma 0 88 1
memory
の enabled
が 1
になっていない場合は対応が必要です。
Raspberry Pi を Raspbian で起動した場合は、デフォルトでは 0 になっているはずです。
その場合、下記の対応をします。
$ vim /boot/firmware/cmdline.txt
末尾に cgroup_memory=1 cgroup_enable=memory
を追記
マシンをリブートする
$ curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik --disable=servicelb" sh -s - --write-kubeconfig-mode 644
traefik
と servicelb
を無効化して、K3s をインストールします。
$ k3s kubectl get all --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system pod/coredns-ccb96694c-txhmg 1/1 Running 0 42s
kube-system pod/local-path-provisioner-5b5f758bcf-v5rln 1/1 Running 0 42s
kube-system pod/metrics-server-7bf7d58749-nqqpn 1/1 Running 0 42s
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default service/kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 60s
kube-system service/kube-dns ClusterIP 10.43.0.10 <none> 53/UDP,53/TCP,9153/TCP 55s
kube-system service/metrics-server ClusterIP 10.43.58.13 <none> 443/TCP 55s
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
kube-system deployment.apps/coredns 1/1 1 1 56s
kube-system deployment.apps/local-path-provisioner 1/1 1 1 55s
kube-system deployment.apps/metrics-server 1/1 1 1 55s
NAMESPACE NAME DESIRED CURRENT READY AGE
kube-system replicaset.apps/coredns-ccb96694c 1 1 1 42s
kube-system replicaset.apps/local-path-provisioner-5b5f758bcf 1 1 1 42s
kube-system replicaset.apps/metrics-server-7bf7d58749 1 1 1 42s
1分ほどで、3つのPod が起動します。
https://github.com/kubernetes/ingress-nginx/releases このページを見て、Ingress Nginx の最新バージョンのバージョン番号を確認します。
さきほどのバージョン番号のマニフェストを使ってインストールします。
$ k3s kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.12.0/deploy/static/provider/baremetal/deploy.yaml
$ k3s kubectl get all --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
ingress-nginx pod/ingress-nginx-admission-create-wc54d 0/1 Completed 0 2m43s
ingress-nginx pod/ingress-nginx-admission-patch-lgd2t 0/1 Completed 1 2m43s
ingress-nginx pod/ingress-nginx-controller-6887c6d764-4whjs 1/1 Running 0 2m43s
kube-system pod/coredns-ccb96694c-txhmg 1/1 Running 0 6m5s
kube-system pod/local-path-provisioner-5b5f758bcf-v5rln 1/1 Running 0 6m5s
kube-system pod/metrics-server-7bf7d58749-nqqpn 1/1 Running 0 6m5s
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default service/kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 6m24s
ingress-nginx service/ingress-nginx-controller NodePort 10.43.13.194 <none> 80:31119/TCP,443:32508/TCP 2m45s
ingress-nginx service/ingress-nginx-controller-admission ClusterIP 10.43.17.117 <none> 443/TCP 2m45s
kube-system service/kube-dns ClusterIP 10.43.0.10 <none> 53/UDP,53/TCP,9153/TCP 6m19s
kube-system service/metrics-server ClusterIP 10.43.58.13 <none> 443/TCP 6m19s
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
ingress-nginx deployment.apps/ingress-nginx-controller 1/1 1 1 2m44s
kube-system deployment.apps/coredns 1/1 1 1 6m20s
kube-system deployment.apps/local-path-provisioner 1/1 1 1 6m19s
kube-system deployment.apps/metrics-server 1/1 1 1 6m19s
NAMESPACE NAME DESIRED CURRENT READY AGE
ingress-nginx replicaset.apps/ingress-nginx-controller-6887c6d764 1 1 1 2m44s
kube-system replicaset.apps/coredns-ccb96694c 1 1 1 6m6s
kube-system replicaset.apps/local-path-provisioner-5b5f758bcf 1 1 1 6m6s
kube-system replicaset.apps/metrics-server-7bf7d58749 1 1 1 6m6s
NAMESPACE NAME STATUS COMPLETIONS DURATION AGE
ingress-nginx job.batch/ingress-nginx-admission-create Complete 1/1 16s 2m44s
ingress-nginx job.batch/ingress-nginx-admission-patch Complete 1/1 18s 2m44s
ingress-nginx の、 Service と Deployment が作られます。
下記コマンドを実行して、 404 レスポンスが取得できることを確認しておきます。
$ curl https://127.0.0.1:32508 --insecure
※ 32508 となっている箇所は、service が 443 を NodePort として公開しているポートを指定します。ランダムで変わります。
$ curl https://127.0.0.1:32508 --insecure
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
さきほど、Deployment と Service を作り動作検証しましたが、今回は使わないので削除します。
$ k3s kubectl delete service -n ingress-nginx ingress-nginx-controller
$ k3s kubectl delete deployment -n ingress-nginx ingress-nginx-controller
先ほどインストールしたマニフェスト の deployment を元に、daemonset のマニフェストを作ってファイルとして保存します。
v1.12.0 の場合、下記のようになりました。
ingress-nginx-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet # 修正
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.12.0
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
minReadySeconds: 0
revisionHistoryLimit: 10
selector:
matchLabels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
# strategy: # 消す
# rollingUpdate:
# maxUnavailable: 1
# type: RollingUpdate
template:
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.12.0
spec:
hostNetwork: true # 追加
containers:
- args:
- /nginx-ingress-controller
- --election-id=ingress-nginx-leader
- --controller-class=k8s.io/ingress-nginx
- --ingress-class=nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: LD_PRELOAD
value: /usr/local/lib/libmimalloc.so
image: registry.k8s.io/ingress-nginx/controller:v1.12.0@sha256:e6b8de175acda6ca913891f0f727bca4527e797d52688cbe9fec9040d6f6b6fa
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
livenessProbe:
failureThreshold: 5
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
name: controller
ports:
- containerPort: 80
hostPort: 80 # 追加
name: http
protocol: TCP
- containerPort: 443
hostPort: 443 # 追加
name: https
protocol: TCP
- containerPort: 8443
hostPort: 8443 # 追加
name: webhook
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
requests:
cpu: 100m
memory: 90Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
readOnlyRootFilesystem: false
runAsGroup: 82
runAsNonRoot: true
runAsUser: 101
seccompProfile:
type: RuntimeDefault
volumeMounts:
- mountPath: /usr/local/certificates/
name: webhook-cert
readOnly: true
dnsPolicy: ClusterFirst
nodeSelector:
kubernetes.io/os: linux
serviceAccountName: ingress-nginx
terminationGracePeriodSeconds: 300
volumes:
- name: webhook-cert
secret:
secretName: ingress-nginx-admission
$ sudo k3s kubectl apply -f ingress-nginx-daemonset.yaml
$ k3s kubectl get all --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
ingress-nginx pod/ingress-nginx-admission-create-wc54d 0/1 Completed 0 10m
ingress-nginx pod/ingress-nginx-admission-patch-lgd2t 0/1 Completed 1 10m
ingress-nginx pod/ingress-nginx-controller-lbptk 1/1 Running 0 31s
kube-system pod/coredns-ccb96694c-txhmg 1/1 Running 0 13m
kube-system pod/local-path-provisioner-5b5f758bcf-v5rln 1/1 Running 0 13m
kube-system pod/metrics-server-7bf7d58749-nqqpn 1/1 Running 0 13m
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default service/kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 14m
ingress-nginx service/ingress-nginx-controller-admission ClusterIP 10.43.17.117 <none> 443/TCP 10m
kube-system service/kube-dns ClusterIP 10.43.0.10 <none> 53/UDP,53/TCP,9153/TCP 13m
kube-system service/metrics-server ClusterIP 10.43.58.13 <none> 443/TCP 13m
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
ingress-nginx daemonset.apps/ingress-nginx-controller 1 1 1 1 1 kubernetes.io/os=linux 31s
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
kube-system deployment.apps/coredns 1/1 1 1 13m
kube-system deployment.apps/local-path-provisioner 1/1 1 1 13m
kube-system deployment.apps/metrics-server 1/1 1 1 13m
NAMESPACE NAME DESIRED CURRENT READY AGE
kube-system replicaset.apps/coredns-ccb96694c 1 1 1 13m
kube-system replicaset.apps/local-path-provisioner-5b5f758bcf 1 1 1 13m
kube-system replicaset.apps/metrics-server-7bf7d58749 1 1 1 13m
NAMESPACE NAME STATUS COMPLETIONS DURATION AGE
ingress-nginx job.batch/ingress-nginx-admission-create Complete 1/1 16s 10m
ingress-nginx job.batch/ingress-nginx-admission-patch Complete 1/1 18s 10m
$ curl 'https://127.0.0.1' --insecure
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
:443 をリッスンするようになりました。
これで、本題は終了です。
試しに、簡単なエコーサーバーを起動し、HTTPリクエストを発行してみると、x-real-ip
と x-forwarded-for
が期待通りにリモートクライアントのIPアドレスになっているのが確認できます。
$ cat /etc/rancher/k3s/k3s.yaml
リモートから使う場合は、server: https://127.0.0.1:6443
となっている箇所の IPアドレスを、外部から見た IPアドレスに変更してください。
$ /usr/local/bin/k3s-uninstall.sh
確認無しで全部消えるので注意してください。
コメント