オンプレでk8sを動かす – 令和最新版

長らくこのブログもストップしていましたがこの度ごっそり構成を変えて復活したのでまとめます。

これまでの構成

旧バージョン(ESXi + kubespray + Istio)

以下のような構成になっていました

(構成図を書くたびに世の中の人々の作図能力の高さが身に染みる。いたい)

old-arch

ESXi上にVMをぽちぽち建てた後にkubesprayでk8sを構成してistio-ingressgatewayをNodePortでデプロイしてそこからリクエストを振り分けていました

この構成で運用する上で以下の辛さがあった

  • なるべく構成をコード管理をしたかった
    • ESXiのterraform-providerがあるにはあるものの公式のものではないこと
    • その分ドキュメントも少ない
  • 無料版だったので作成可能なVMのサイズに制限があった
  • kubesprayが一発で通ることが無くてググりながら修正して通すのが地味に面倒だった

新バージョン(KVM/QEMU + Opennebula + Rancher + nginx-ingress)

new-arch

この構成にしてよくなったところ

  • rancherでk8sクラスタを立ち上げるようにしたことでそもそもterraformが不要になった
  • ノードの増減がGUIでポチポチで良くなった
  • ノードが落ちてしまった時rancherが勝手に新しいノードを作成して割り当ててくれる

手順

以下の順でインストールしていった (当たり前といえばそう)

  1. KVM/QEMU
  2. Opennebula
  3. Rancher
  4. nginx-ingress-controller

KVM/QEMU

ここで詰まる事はなかった

ググったらいくらでも手順が出てきた

強いて言えばopennebulaがubuntu 22.04だと上手く動かなかったので一度20.04をインストールし直す必要があったくらい

備忘録も兼ねているので一応参考リンクを貼っておく

https://ubuntu.com/blog/kvm-hyphervisor

Opennebula

本家のドキュメントを見ながらインストールした

minioneを使ってポンって最初やっていたがやってくうちによくわからんくなったので順に手動でインストールした

https://docs.opennebula.io/6.4/installation_and_configuration/frontend_installation/install.html

物理マシンは1台なのでfrontendと本体?も一緒にインストールした

ここもそんなに躓くことはなかった。

rancherを動かす用のVMをfirecrackerで動かそうとしてたもののKVMと共存ができないらしく諦めた

ケチ臭い人間なのでdocker-machineでわざわざ専用のVMを動かすのもその分のオーバーヘッドが嫌だったのでホスト上で直接docker動かしてrancherを動かしている

今のところ問題ない

rancher

このあたりから結構躓いた

基本は以下のドキュメントを見ながらインストールした

https://docs.opennebula.io/6.4/integration_and_development/automation_tools_integration/rancher.html

rancherの起動

まずdockerを動かすときに --plivilegedオプションが必要だった

また、opennebulaを80番で動かしていてポートが被ったので別ポートで動かしている

バージョンは固定しないと発作が出るので今回はv2.6.9にしている

docker run -d --restart=unless-stopped --privileged -p 8080:80 -p 8443:443 rancher/rancher:v2.6.9

node driverのインストール

ドキュメントにあるURL(https://downloads.opennebula.io/packages/opennebula-6.4.0/opennebula-docker-machine-6.4.0.tar.gz)をそのまま使うと上手く読み込めていなかった

どうも複数のバイナリが入っていていい感じに選べなかったらしい

ホストのアーキテクチャに合うバイナリだけ選んでtarで固めた物を適当な所においてhttpで参照できるようにしてそのURLをDownloadURLとして指定すると上手くいった

クラスタの作成

k8sのノードに使用するイメージもubuntu 22.04を使おうとするとdockerのインストールまでは上手くいくのにsshが途中で出来なくなってこけていた

ubuntu 20.04を使うとすんなり上手くいった

node templateの設定でtemplateを使うと上手くいかなかったのでimage id, network idを指定して設定した

最終的には以下のようになった

NodeTemplates > View API > Editで出てくる情報

(環境次第で可変になりそうなところはマスクしてます)

{"b2dSize":"","cpu":"4","devPrefix":"vd","disableVnc":false,"diskResize":"20480","imageId":"0","imageName":"","imageOwner":"oneadmin","memory":"16384","networkId":"<networkId>","networkName":"","networkOwner":"<networkOwner>","password":"<opennebulaのpassword>","sshUser":"<VMのssh可能なユーザー>","startRetries":"600","templateId":"","templateName":"","user":"<opennebulaのユーザー>","vcpu":"4","xmlrpcurl":"http://<host ip>:2633/RPC2"}

クラスタの設定

nginx-ingressはデフォルトでインストールされてしまうが、自分でいろいろ設定してまとめてインストールしたかったのでデフォルトでインストールされないようにした。

disable-nginx-ingress

ここまででk8s側はだいたいOK

サービスのデプロイ

サービスを外部公開する為の物をデプロイ

nginx-ingress

helm-chartを手元に置いておきたかったのでkubernetes/ingress-nginxのリポジトリからクローンしてきてvaluesを少し弄った

https://github.com/kubernetes/ingress-nginx/tree/main/charts/ingress-nginx

変更点を簡単に説明すると

  • nginx-ingress-controllerをdaemonsetでデプロイ
    • ロードバランサを用意するのもノードポートでアクセスするようにするのもちょっと面倒だし気に食わなかった
  • ssl-redirectの無効化
    • デフォルトでnginx-ingress-controllerがhttpsへのリダイレクトをするらしい
    • wordpressとかアプリ側でもリダイレクトがあったりすると無限ループが発生していてややこしかったので無効化した
    • 多分cloudflareとも無限ループしてるケースがあった気がする
    • cert-managerも上手く動いていなかったような気もする(ここは記憶が曖昧)
  • nodeSelectorでmasterノードにのみデプロイされるように
    • 外部のnginxでリクエストをそれぞれのノードに振り分けるのにノードのIPはある程度定まっていてほしかった
    • workerは結構増減しそうだったのでmasterのみにデプロイ
  • minecraft用に25565ポートを追加で空ける
diff --git a/charts/ingress-nginx/values.yaml b/charts/ingress-nginx/values.yaml
index 35f922f6b..7a5846fe9 100644
--- a/charts/ingress-nginx/values.yaml
+++ b/charts/ingress-nginx/values.yaml
@@ -41,9 +41,15 @@ controller:
   containerPort:
     http: 80
     https: 443
+    minecraft: 25565
   # -- Will add custom configuration options to Nginx https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
-  config: {}
+  config: {
+      "ssl-redirect": "false", 
+      "proxy-body-size": "0",
+      "proxy-connect-timeout": "600s",
+      "proxy-read-timeout": "600s",
+  }

   # -- Annotations to be added to the controller config configuration configmap.
   configAnnotations: {}
@@ -92,12 +98,13 @@ controller:
   ## Disabled by default
   hostPort:
     # -- Enable 'hostPort' or not
-    enabled: false
+    enabled: true
     ports:
       # -- 'hostPort' http port
       http: 80
       # -- 'hostPort' https port
       https: 443
+      minecraft: 25565

   # -- Election ID to use for status update, by default it uses the controller name combined with a suffix of 'leader'
   electionID: ""
@@ -191,7 +198,7 @@ controller:
   #         name: secret-resource

   # -- Use a `DaemonSet` or `Deployment`
-  kind: Deployment
+  kind: DaemonSet

   # -- Annotations to be added to the controller Deployment or DaemonSet
   ##
@@ -290,7 +297,7 @@ controller:
   ## Ref: https://kubernetes.io/docs/user-guide/node-selection/
   ##
   nodeSelector:
-    kubernetes.io/os: linux
+    node-role.kubernetes.io/controlplane: "true"

   ## Liveness and readiness probe values
   ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes
@@ -498,12 +505,14 @@ controller:
     ports:
       http: 80
       https: 443
+      minecraft: 25565

     targetPorts:
       http: http
       https: https
+      minecraft: minecraft

-    type: LoadBalancer
+    type: ClusterIP

     ## type: NodePort
     ## nodePorts:
@@ -935,7 +944,8 @@ imagePullSecrets: []
 # -- TCP service key-value pairs
 ## Ref: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/exposing-tcp-udp-services.md
 ##
-tcp: {}
+tcp:
+  25565: "external-service/forgemc:25565"
 #  8080: "default/example-tcp-svc:9000"

 # -- UDP service key-value pairs

valuesの変更が出来たら以下のコマンドでデプロイする

helm upgrade --install ingress-nginx path/to/chart --namespace ingress-nginx --create-namespace

ここまででk8sのmasterノードになってるIPの80,443ポートでリクエストを受けられるようになっている

この状態ではリクエストの振り分け先が無いのでデフォルトバックエンドに降られて404が返る 404

動作確認用のbootcampのデプロイ

動作確認用にbootcampをデプロイする

yamlは以下の通り

bootcampフォルダを作ってそこに配置する

  • 後でapplyが楽なので

bootcamp/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: bootcamp
  name: bootcamp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: bootcamp
  template:
    metadata:
      labels:
        app: bootcamp
    spec:
      containers:
      - image: gcr.io/google-samples/kubernetes-bootcamp:v1
        name: bootcamp
        ports:
        - containerPort: 8080
      dnsPolicy: ClusterFirst
      restartPolicy: Always

bootcamp/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: bootcamp
  labels:
    app: bootcamp
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: bootcamp

bootcamp/ingress.yaml

cert-managerの設定とかも入ってるけどこれは今度また書く

kind: Ingress
metadata:
  name: bootcamp
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/issuer: "letsencrypt"
spec:
  tls:
  - hosts:
    - home.ideta.net
    secretName: bootcamp-tls
  rules:
  - host: home.ideta.net
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: bootcamp
            port:
              number: 80

デプロイ

kubectl apply -f bootcamp

チェック

ingressがhostにIPの指定を許していないのでとりあえず適当に名前を割り当てておく

ちゃんとノードにリクエストが飛ぶ名前にしておけばこれでもうつながる

ただし、うちには外部接続性のない貧しい家庭なので追加の手順が必要になる

各ノードにリクエストを振り分けるnginxの設定

うちは家に外部からの接続性がないのでリクエストを受け付ける用のVMをvultrに用意している

k8sのクラスタ外にあるそのVMでnginxを動かしてそこからVPN経由でクラスタにリクエストをプロキシしている

自分の場合は以下みたいな感じで外から接続する用のnginxが各masterノードにリクエストを振り分けてる

  • 厳密にはVPNで繋いでいるプロキシ用のVMが宅内側にもう一台いるがやっていることは同じくVPN経由の外からのリクエストをnginxでプロキシしているだけなので省略
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
        # multi_accept on;
}

stream {
  upstream http {
    server 10.1.0.1:80 max_fails=10 fail_timeout=10;
    server 10.1.0.2:80 max_fails=10 fail_timeout=10;
    server 10.1.0.3:80 max_fails=10 fail_timeout=10;
  }
  server {
    listen 80;
    proxy_pass http;
  } 
  upstream https {
    server 10.1.0.1:443 max_fails=10 fail_timeout=10;
    server 10.1.0.2:443 max_fails=10 fail_timeout=10;
    server 10.1.0.3:443 max_fails=10 fail_timeout=10;
  }
  server {
    listen 443;
    proxy_pass https;
  } 
}

この状態で http://home.ideta.net に接続するとbootcampにつながっている

リロードしてると2個あるpodにいい感じに振り分けられてるのを感じられる

bootcamp.png

力尽きたのでcert-managerの設定の話とその他諸々はまた書く

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です