はじめに
Kubernetes (k8s) のPOD間でssh接続できるように設定してみます。
minikubeのインストール
k8sの環境を作るのにminikubeを利用します。
https://minikube.sigs.k8s.io/docs/start/
を参考にインストールします。LinuxとWindows、macOSに対応しているようです。 ここでは、Windowsで試してみます。仮想マシンが必要ですのでVirtualBoxなどをインストールしておきましょう。 インストールが完了したら、クラスタを開始します。 Windowsなのでコマンドプロンプトを開いて、
minikube startを実行します。 自動的に準備が始まります。最初にいろいろダウンロードしますが、大きいものは、
- VMブートイメージ(minikube-v1.16.0) 212.6MiB
- Kubernetes(v1.20.0) 491MiB
minikube kubectl --get pods -Aを実行すると、39.52MiBのダウンロードが始まり、完了するとkubectlコマンドを使えるようになります。 上記コマンド自体は
NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-74ff55c5b-srhzs 1/1 Running 0 2m35s kube-system etcd-minikube 1/1 Running 0 2m50s kube-system kube-apiserver-minikube 1/1 Running 0 2m50s kube-system kube-controller-manager-minikube 1/1 Running 0 2m50s kube-system kube-proxy-s8wbb 1/1 Running 0 2m35s kube-system kube-scheduler-minikube 1/1 Running 0 2m50s kube-system storage-provisioner 1/1 Running 1 2m50sという出力をします(出力前にコマンドがなかったら自動でダウンロードされるということのようです)。
sshキーの作成
コンテナ内にアクセスできるユーザを制限するため、sshの公開鍵と秘密鍵を作成しておきます。 Linuxではssh-keygenコマンドで作成できます。Windows環境ならWSL1で動作しているLinuxで作成すれば良いでしょう。詳細は省略しますが、ここでは id_rsa_xyz というキーを作成したとして進めます。
Pod上で動作させるDockerイメージの作成
https://minikube.sigs.k8s.io/docs/handbook/pushing/によると、 minikubeにssh接続してDockerのイメージを作るのがもっとも簡単のようです。
minikube sshでminikubeのノード内(VirtualBox内の環境)に入ることができます。ただし、コマンドプロンプトから入るとエスケープシーケンスが処理できず、バックスペースで文字の削除すらできません。 WindowsではWSL1が利用できるので、そこから入ることで回避します(WSL1の準備方法は省略)。WSLのターミナルを立ち上げて(ここではwslttyを利用)、同じコマンドを実行すると、今度はエスケープシーケンスの処理が正しくできます。 どのユーザでログインしたのかは、
$ pwd /home/docker $ who docker pts/0 xxxxxxで確認できます。接続先のユーザはdockerになっているようです。 c:\Usersが最初からマウントされていて、minikubeのノード内からは/c/Users/...でアクセスできますので、これを使えばホストであるWindowsとファイルのやりとりができます。 このディレクトリに先程作成したsshキーの公開鍵(ここではid_rsa_xyz.pub)をコピーしておきます。 次に、sshサーバを動作させるDockerイメージを作成します。今回はWindows上でsshsrv.dockerファイルを以下の内容で作成します。
FROM ubuntu:20.04 RUN apt-get update && apt-get install -y openssh-server RUN mkdir /var/run/sshd RUN useradd -m xyz && echo "xyz:xyz" | chpasswd RUN mkdir -p /home/xyz/.ssh && chown xyz /home/xyz/.ssh && chmod 700 /home/xyz/.ssh ADD ./id_rsa_xyz.pub /home/xyz/.ssh/authorized_keys RUN chown xyz /home/xyz/.ssh/authorized_keys && chmod 600 /home/xyz/.ssh/authorized_keys RUN bash -c "echo ForwardAgent yes" > ~/.ssh/config CMD ["/usr/sbin/sshd", "-D"]sshサーバの設定は
https://qiita.com/YumaInaura/items/1d5c18a9e55484ccad89
http://kotaroito.hatenablog.com/entry/2016/03/07/094423
を参考にしました。 なお、3行目の /var/run/sshd がないと
Missing privilege separation directory: /run/sshdとメッセージが出てsshdを起動できません。 Dockerイメージを作成します。
docker build -f sshsrv.docker -t sshsrv:v1 .sshdをコンテナ内で実行してみます。
docker run -d -p 222:22 --name sshsrv sshsrv:v1-dはコンテナをバックグラウンドで動かすための引数です。付けわすれるとそのターミナルでは操作できなくなります(Ctrl+DもCtrl+Cも効かない )。--nameで名前を付けておくと、docker psしたときに名前が表示されます。 コンテナ外からsshできるか試してみます。Windowsからアクセスするわけではなく、minikubeのノード内からsshsrvコンテナにアクセスします。 アクセスには秘密鍵かつパーミッションが適切である必要があるので、minikubeのノード内に秘密鍵であるid_rsa_xyzをコピーしてパーミッションも設定しておきます。
cp /c/Users/path/to/id_rsa_xyz ~/.ssh/ chmod 600 ~/.ssh/id_rsa_xyz ssh-agent bash ssh-add ~/.ssh/id_rsa_xyz ssh -p 222 xyz@localhostとすると、動いているコンテナの中に入ることができます。 動作確認ができましたので、コンテナ内から抜けて、今のコンテナは停止しておきます。
docker kill sshsrv docker rm sshsrvあとでコンテナ間接続で使うので、.ssh/configも設定しておきます。
echo "ForwardAgent yes" > ~/.ssh/config chmod 600 ~/.ssh/configなお、できあがったイメージの中にsshを使わずに入るには、
docker run --rm -it sshsrv:v1 /bin/bashを実行すれば中にはいれますが、こちらは今回の目的とは異なりますので使う必要はありません。
k8sで実行
sshサーバを動かすコンテナが準備できましたので、ここではk8s経由で複数のsshサーバを立ててみます。 今回は試しに動作させて見るだけですので、Deploymentを使わずReplicaSetを使ってみます。 まず、k8sに指示するためのsshsrv.yamlファイルを作成します。
apiVersion: apps/v1 kind: ReplicaSet metadata: name: sshsrvset spec: selector: matchLabels: app: sshsrv-template-label replicas: 3 template: metadata: labels: app: sshsrv-template-label spec: containers: - name: sshsrv-template image: sshsrv:v1 ports: - containerPort: 22spec.template.metadata.labels に記述しているapp: sshsrv-template-labelがコンテナのテンプレート名です。 spec.template.spec 以下に、コンテナの設定が記述されています。 spec.template.spec.containers.name がテンプレートそのものの名前で、k8sが立ち上げるコンテナの名前の一部になります。 spec.template.spec.containers.image は前節で作成したDockerイメージの名前で、ここで指定したDockerイメージからコンテナが作成されます。 spec.template.spec.containers.ports のcontainerPort:22はssh接続用のポート番号となります。コメントアウトしてもsshでアクセスできるのでなくても良いのかもしれません。 spec.selector.matchLabels の app: sshsrv-template-labelは spec.template.metadata.labels に記載した内容と同じにします。このラベルを使って、PODに適用するテンプレートを探しているようです。同じyamlファイル中のtemplateが1個だけなら原理的にはtemplateは一意に特定できるので、わざわざlabelを設定する必要はなさそうにも思うのですが、selectorとtemplateの両方を書かないと動作しないので仕方がありません。 spec/replicas の3は、3個同じコンテナを立ち上げることを指示しています。 さて、yamlファイルが準備できましたので、コマンドプロンプトから実行します(WSL1からだとパスが処理できずminikubeを実行できません)。
minikube kubectl -- apply -f sshsrv.ymlminikube sshでminikubeのノード内に入って、docker ps -aを実行すると、
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f14472bc0ace 66c4d55614da "/usr/sbin/sshd -D" 3 minutes ago Up 3 minutes k8s_sshsrv-template_sshsrvset-m8784_ default_48b8f9f7-0c48-4681-957e-96aad5d7ac57_0 8b9c796bc762 66c4d55614da "/usr/sbin/sshd -D" 3 minutes ago Up 3 minutes k8s_sshsrv-template_sshsrvset-t79pr_ default_72cb56bd-d4d8-42f5-8378-34ef7b6d9a1a_0 97849d1f2fbc 66c4d55614da "/usr/sbin/sshd -D" 3 minutes ago Up 3 minutes k8s_sshsrv-template_sshsrvset-wgqpr_ default_62c6fcee-26ac-4799-8e3d-59749ef8a3db_0のような表示が得られます。3つ立ち上がっていることが確認できます。コマンドプロンプトに戻って
minikube kubectl -- get pods -o wide -l app=sshsrv-template-labelを実行すると、k8s上では
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES sshsrvset-m8784 1/1 Running 0 3m 172.17.0.5 minikubeのような名前でコンテナが立ち上がっていることがわかります。IPアドレスも別々に割り振られているので、 minikubeのノード内からsshsrvset-t79pr 1/1 Running 0 3m 172.17.0.4 minikube sshsrvset-wgqpr 1/1 Running 0 3m 172.17.0.3 minikube
$ ssh xyz@172.17.0.5のようにすると、コンテナ内に接続できます。 コンテナを止めるには、コマンドプロンプトから
minikube kubectl -- delete replicaset sshsrvsetを実行します。
minikubeのノード内から各コンテナにssh
前節のようにコンテナを立ち上げても、IPアドレスがわからない場合には接続できません。 そこで、ランダム接続にはなってしまいますが、Serviceを作ります。 Serviceをつくると各コンテナに直接接続せず、Serviceで作成したIPアドレスとポートにつなぎにいけば、コンテナのいずれかに接続することができます。 まず、sshd-service.yamlを作成します。
apiVersion: v1 kind: Service metadata: name: sshd-service spec: type: ClusterIP selector: app: sshsrv-template-label ports: - protocol: TCP port: 22 targetPort: 22重要な点は、spec.selectorに書くラベルはReplicaSetのspec.template.metadata.labelsに記載しているラベルということです。 ReplicaSetのmetadata.nameではありません。また、ReplicaSetのyamlにmetadata.labelsの項目を作ってそこにラベルを書いてもそこは検索対象になりません。 なぜ、template側を参照する設計になっているのかは謎です。高度すぎてまるで分かりません。 さて、コマンドプロンプトからServiceを立ち上げます。
minikube kubectl -- apply -f sshd-service.yaml立ち上げ後、
minikube kubectl -- get service -o wideを実行すると表示されるsshd-serviceの行のCLUSTER-IPをたたけば、ReplicaSetで立ち上げたコンテナのいずれかにランダム(?)で接続できます。
ssh xyz@xxx.xxx.xxx.xxxxxx.xxx.xxx.xxxにはCLUSTER-IPに表示されたIPアドレスを入れます。 コンテナの中からさらに
ssh xyz@xxx.xxx.xxx.xxxを実行すると、同様に接続できます。確証はありませんが、20回試した範囲では、接続先からは自分自身は除かれているようです。 ただし、2段の場合は自分自身に戻ってくることがあります。 コンテナA→コンテナB→コンテナAというパターンはあるが、コンテナA→コンテナAはなさそうということです。 ランダムではなく各ノードの負荷状況を考慮して接続するサーバを選びたいのですが、残念ながら方法がわかりませんでした。 コンテナ内からであれば、IPアドレスではなくドメイン名でアクセスすることもできます。
ssh xyz@sshd-service.default.svc.cluster.localまたは、サービス名のみで
ssh xyz@sshd-serviceでアクセスすることもできます。コンテナ内で/etc/resolv.confの中身を表示すれば、ドメイン名の解決のための 設定がどのようになっているのかが分かるかと思います。 Serviceの停止は、
minikube kubectl -- delete service sshd-serviceで停止できます。
参考
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/ がk8sのAPIリファレンスです。kindがたくさんありますが、各kindがどういう役割のものなのかは、ここには記載されていません。yamlに記載する各フィールドの説明はしっかり記載されていますので、各kindを理解してから読むようにしましょう。 各kindがどういうものなのかはは、https://kubernetes.io/ja/docs/home/の検索窓にkind名を入力して検索すれば説明ページがでてきますので、それを参考にしましょう。 DeploymentとStatefulSetとJobだけはAPIリファレンスに短い解説がありましたので引用します。
- Deployments for stateless persistent apps (e.g. HTTP servers).
- StatefulSets for stateful persistent apps (e.g. databases).
- Jobs for run-to-completion apps (e.g. batch Jobs).
Kind | 説明 |
---|---|
Pod | Pod は、Kubernetesアプリケーションの基本的な実行単位です。これは、作成またはデプロイするKubernetesオブジェクトモデルの中で最小かつ最も単純な単位です。Podは、クラスターで実行されているプロセスを表します。 https://kubernetes.io/ja/docs/concepts/workloads/pods/pod-overview/ |
Job | Jobは1つ以上のPodを作成し、指定された数のPodが正常に終了することを保証します。 JobはPodの正常終了を追跡します。正常終了が指定された回数に達すると、そのタスク(つまりJob)は完了します。Jobを削除すると、そのJobが作成したPodがクリーンアップされます。簡単な例としては、1つのPodを確実に実行して完了させるために、1つのJobオブジェクトを作成することです。ノードのハードウェア障害やノードの再起動などにより最初のPodが失敗したり削除されたりした場合、Jobオブジェクトは新たなPodを立ち上げます。また、Jobを使用して複数のPodを並行して実行することもできます。 https://v1-18.docs.kubernetes.io/ja/docs/concepts/workloads/controllers/job/ |
ReplicaSet | ReplicaSetの目的は、どのような時でも安定したレプリカPodのセットを維持することです。これは、理想的なレプリカ数のPodが利用可能であることを保証するものとして使用されます。 https://kubernetes.io/ja/docs/concepts/workloads/controllers/replicaset/ |
Deployment | Deployment はPodとReplicaSetの宣言的なアップデート機能を提供します。Deploymentにおいて 理想的な状態 を記述すると、Deploymentコントローラーは指定された頻度で現在の状態を理想的な状態に変更します。Deploymentを定義することによって、新しいReplicaSetを作成したり、既存のDeploymentを削除して新しいDeploymentで全てのリソースを適用できます。 https://kubernetes.io/ja/docs/concepts/workloads/controllers/deployment/ https://qiita.com/tkusumi/items/01cd18c59b742eebdc6a |
StatefulSet | StatefulSetはステートフルなアプリケーションを管理するためのワークロードAPIです。StatefulSetはDeploymentとPodのセットのスケーリングを管理し、それらのPodの順序と一意性を保証します。 Deploymentのように、StatefulSetは指定したコンテナのspecに基づいてPodを管理します。Deploymentとは異なり、StatefulSetは各Podにおいて管理が大変な同一性を維持します。これらのPodは同一のspecから作成されますが、それらは交換可能ではなく、リスケジュール処理をまたいで維持される永続的な識別子を持ちます。ワークロードに永続性を持たせるためにストレージボリュームを使いたい場合は、解決策の1つとしてStatefulSetが利用できます。StatefulSet内の個々のPodは障害の影響を受けやすいですが、永続化したPodの識別子は既存のボリュームと障害によって置換された新しいPodの紐付けを簡単にします。 https://kubernetes.io/ja/docs/concepts/workloads/controllers/statefulset/ |
Service | Podの集合で実行されているアプリケーションをネットワークサービスとして公開する抽象的な方法です。Kubernetesでは、なじみのないサービスディスカバリーのメカニズムを使用するためにユーザーがアプリケーションの修正をする必要はありません。 KubernetesはPodにそれぞれのIPアドレス割り振りや、Podのセットに対する単一のDNS名を提供したり、それらのPodのセットに対する負荷分散が可能です。 https://kubernetes.io/ja/docs/concepts/services-networking/service/ |
Secret | KubernetesのSecretはパスワード、OAuthトークン、SSHキーのような機密情報を保存し、管理できるようにします。 Secretに機密情報を保存することは、それらをPodの定義やコンテナイメージに直接記載するより、安全で柔軟です。 https://kubernetes.io/ja/docs/concepts/secret/configuration/ |
ConfigMap | ConfigMapは、機密性のないデータをキーと値のペアで保存するために使用されるAPIオブジェクトです。Podは、環境変数、コマンドライン引数、またはボリューム内の設定ファイルとしてConfigMapを使用できます。 https://kubernetes.io/ja/docs/concepts/configuration/configmap/ |
Ingress | クラスター内のServiceに対する外部からのアクセス(主にHTTP)を管理するAPIオブジェクトです。Ingressは負荷分散、SSL終端、名前ベースの仮想ホスティングの機能を提供します。 https://kubernetes.io/ja/docs/concepts/services-networking/ingress/ |
0 件のコメント :
コメントを投稿