この記事はなに?
関わっているプロジェクトで,社内のオンプレミス環境にKubernetesの構築をする機会がありました.
まず一度,自分の手元でテスト用環境を構築しようと思い,Google Compute Engineを使ってVMを立ち上げ,そこにKubernetesをインストールしてみたところ成功したので,手順をメモしておきます.
Kubernetes環境の構築には,Kubesprayを利用しています.
Kubesprayは,マルチノードからなるKubernetesクラスタを比較的少ない手順で構築するための,AnsibleのPlaybookとその周辺ツールです.
https://kubernetes.io/docs/setup/production-environment/tools/kubespray/
https://github.com/kubernetes-sigs/kubespray
https://kubespray.io/#/
環境
- Kubespray v2.11.0
- Google Compute Engine(以下,「GCE」).ゲストOSのVMはUbuntu 16.04のイメージを利用
- Kubernetes 1.15.3
今回やりたいこと
図にするとこんな感じ↓
ここで,
- k8s-admin: Adminサーバ.Kubesprayを用いたKubernetesのインストール作業や,その後の作業を行うための作業用のサーバ.このサーバは今回インストールするKubernetesクラスタの外に位置することになる.
- k8s-master[1-2]: KubernetesのMasterノードとなるサーバ群.
- k8s-node[1-3]: KubernetesのWorkerノードとなるサーバ群.
と定義する.
また,etcdについては,k8s-master[1-2], k8s-node1の3台にインストールすることを想定する(本当はMasterノードを3台用意してそこにインストールするのがよさそう).
手順
GCEでのVMインスタンス構築
-
GCPのプロジェクト設定は完了しているものと仮定する.
まだなら
https://cloud.google.com/compute/docs/quickstart-linux
から設定する. -
「VMインスタンス」 -> 「インスタンスを作成」から,VMインスタンスを作成する.
今回は,以下のようなテンプレートを作り,これを計6台複製して作成することとする.
ここで,「内部IP」の値については,あとで使うので控えておく.
-
作ったVMすべてに,ローカルPCからSSHでログインできることを確かめる.
[user@localhost] # 正しいプロジェクトに接続しているか確かめる gcloud config list # SSHでログイン gcloud beta compute --project "${GCP_PROJECT_NAME}" ssh --zone "us-central1-a" "${VM_INSTANCE_NAME}"
GCEのファイアウォールの設定
今回の手順で一番ハマったところがここ.
GCEでは,デフォルトではTCP,UDP,ICMP以外のプロトコルでの通信をファイアウォールでブロックする仕様になっているらしい.
https://cloud.google.com/vpc/docs/vpc?hl=ja
今回構築するKubernetesのネットワークプラグインであるCalicoでは,(デフォルトで,)ノード間で通信を中継する際に,IPinIPというプロトコルが使われていて,そのままでは,ノード間での通信が発生する場合(例えば,自ノードに存在しないPodのIPアドレスにHTTPでアクセスしようとした時など)に,GCE内のファイアウォールでパケットがブロックされて通信ができないという事象が発生する.
https://github.com/kubernetes-sigs/kubespray/blob/master/docs/calico.md#cloud-providers-configuration
https://docs.projectcalico.org/v1.5/getting-started/docker/installation/gce
https://qiita.com/oke-py/items/395bb294d6d88c28634e
IPinIPをファイアウォールのルールで許可することで,これを回避する.
SSH鍵の生成と公開鍵の配布
Kubesprayでの構築ではAnsibleを利用するため,Adminサーバからクラスタを構成するサーバへのSSHでのリモートログインができることが前提となる.
AdminサーバでSSH鍵を作成し,それを他のサーバに配布することにより,インスタンス間でのSSHができるようにする.
-
AdminサーバでSSH鍵を生成する.今回はテスト用の環境なので,暗号強度等には特にこだわらないこととする.
[user@k8s-admin] ls -l ~/.ssh/id_rsa{,.pub} # 鍵が存在しないことを確認する ssh-keygen # 鍵の配置場所は~/.ssh/id_rsa{,.pub}.パスフレーズ等はすべて空文字列を指定 ls -l ~/.ssh/id_rsa{,.pub} # id_rsa, id_rsa.pubが両方存在することを確認する.id_rsaについては,秘密鍵なので他のユーザが見られないようにしておく cat ~/.ssh/id_rsa.pub # 公開鍵の内容を記録する
-
1.で記録した公開鍵の内容を,クラスタを構成するサーバ群の~/.ssh/authorized_keysに登録する.これによって,Adminサーバと他のサーバ間でのSSH鍵での疎通が取れるようになる.
[user@k8s-master[1-2], k8s-node[1-3]] mkdir ~/.ssh/ # ディレクトリがなければ作る echo ${CONTENTS_OF_PUBLIC_KEY} >> ~/.ssh/authorized_keys
-
SSHのログインができることを確かめる.GCEでは,インスタンス間での内部通信用にIPアドレス(= 内部IP)が振ってあるため,そちらを使ってログインを試みる.
[user@k8s-admin] ssh 10.128.0.20 # 10.128.0.20(= k8s-master1の内部IP)でk8s-master1にパスワードの入力なしでSSHログインできることを確かめる # 以下,すべてのサーバについて同様
他サーバにはじめてSSH接続する際には,確認画面が表示されることがある.これを放置するとその後のplaybookが失敗するので,すべてのサーバについて"yes"を回答し,接続時に確認画面が表示されないようにしておく.
パッケージの更新
ここからは,Adminサーバに必要なソフトウェアをインストールしていく作業になる.
まず,APTのパッケージ情報が古いと後述のPIPがうまく利用できなかったので,Adminサーバについて,パッケージを最新化しておく.
どうやらKubespray側では,apt-get upgradeまではしないようなので,ついでに全台についてやっておいた方がいい.
[user@k8s-admin, k8s-master[1-2], k8s-node[1-3]]
sudo apt-get update && sudo apt-get upgrade
PIPのインストール
Kubesprayの依存ソフトウェアをインストールする際ににPIPが必要.
PythonについてはすでにVMイメージにインストールされていたので,PIPのみを追加でインストールする.
ここからの作業については,基本的にAdminサーバだけで問題ない.
[user@k8s-admin]
# インストールスクリプトのダウンロード
curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
ls -l get-pip.py
# 存在することを確認
# PIPのインストール
sudo python3 get-pip.py
pip -V
# 最新版のPIPがインストールされていることを確認
APTを使ってPIPをインストールすると,なぜか古いバージョンがインストールされ,その後のansible-playbookコマンドが失敗することがあるので注意.
Kubesprayのダウンロード
Githubからダウンロードする.
[user@k8s-admin]
git clone https://github.com/kubernetes-sigs/kubespray.git
cd kubespray
git checkout v2.11.0
git status
# ソースコードがv2.11.0になっていることを確認
依存ソフトウェアをダウンロードする
-
requirements.txtに,Kubesprayが依存するソフトウェアが記されているので,これをPIPでインストールする.
[user@k8s-admin] sudo pip install -r requirements.txt
-
自分の環境では,1.で
ERROR: paramiko 2.6.0 has requirement cryptography>=2.5, but you'll have cryptography 1.2.3 which is incompatible.
のようなエラーが表示された.
理由まで追うことができなかったが,このバージョン非互換を放置すると,後のansible-playbookコマンドが失敗するので,最新版のcryptographyを手動でインストールし直すことで回避する.[user@k8s-admin] # 古いバージョンをアンインストール sudo pip3 uninstall cryptography # 最新バージョンをインストール sudo pip3 install cryptography
ここでAnsible等もインストールされるので,これ以上追加でソフトウェアをインストールする必要はない.
Ansibleの設定を行う
-
inventoryを設定する.Kubesprayのソースコードには,設定ファイル群のテンプレートがあるので,これをコピーしてから編集する形で進める.
[user@k8s-admin] cd ~/kubespray # inventoryのテンプレートをコピーする cp -rfp inventory/sample inventory/mycluster ls -l inventory/{sample,mycluster} # 両方存在することを確認
-
Kubesprayには,IPアドレスの配列を入力することでサンプルのinventoryファイルを自動生成するスクリプトがあるので,それを使って雛形を作り,適宜編集する.
[user@k8s-admin] # サンプルのinventoryファイル(hosts.yml)を作成する declare -a IPS=(10.128.0.20 10.128.0.21 10.128.0.22 10.128.0.23 10.128.0.24) # ここにはAdminサーバ以外(クラスタとなるサーバ)のVMの内部IPを記載する CONFIG_FILE=inventory/mycluster/hosts.yml python3 contrib/inventory_builder/inventory.py ${IPS[@]} vim inventory/mycluster/hosts.yml # 実現したいクラスタ構成に応じてinventoryファイルの内容を編集する
今回構成したいクラスタの場合,こんな感じになるはず(etcdについてはまだ再考が必要かも).
all: hosts: k8s-master1: # ここに指定する名前がKubernetesにおけるノードの名前になる.デフォルトの"node*"から変更している ip: 10.128.0.20 ansible_host: 10.128.0.20 access_ip: 10.128.0.20 k8s-master2: ip: 10.128.0.21 ansible_host: 10.128.0.21 access_ip: 10.128.0.21 k8s-node1: ip: 10.128.0.22 ansible_host: 10.128.0.22 access_ip: 10.128.0.22 k8s-node2: ip: 10.128.0.23 ansible_host: 10.128.0.23 access_ip: 10.128.0.23 k8s-node3: ip: 10.128.0.24 ansible_host: 10.128.0.24 access_ip: 10.128.0.24 children: kube-master: # Masterノードとなるhostを指定 hosts: k8s-master1: k8s-master2: kube-node: # Workerノードとなるhostを指定 hosts: k8s-node1: k8s-node2: k8s-node3: etcd: # etcdを配置するhostを指定 hosts: k8s-master1: k8s-master2: k8s-node1: k8s-cluster: children: kube-master: kube-node: calico-rr: hosts: {}
inventoryの詳細については,
https://github.com/kubernetes-sigs/kubespray/blob/master/docs/ansible.md
を参照すること. -
kubectlの設定を行う.Kubesprayには,kubectlを自動でインストールして,クラスタへの接続設定まで行う機能がある.これを有効にする.
[user@k8s-admin] # 設定ファイルを編集する cp -ip inventory/mycluster/group_vars/k8s-cluster/k8s-cluster.yml{,.bak} vim inventory/mycluster/group_vars/k8s-cluster/k8s-cluster.yml diff inventory/mycluster/group_vars/k8s-cluster/k8s-cluster.yml{,.bak} 184c184 < kubeconfig_localhost: true # inventory/mycluster/artifacts/admin.confにkubectlの設定ファイルをダウンロードする --- > # kubeconfig_localhost: false 186c186 < kubectl_localhost: true # inventory/mycluster/artifacts/kubectlにkubectlをダウンロードする --- > # kubectl_localhost: false
他にもいろいろ設定項目はあるので,カスタムの設定が行いたい場合は適宜確認すること.
AnsibleでKubernetesクラスタを構築する
ansible-playbookを実行して,クラスタにKubernetesをインストールする.
[user@k8s-admin]
ansible-playbook -i inventory/mycluster/hosts.yml --become --become-user=root cluster.yml
VM5台にインストールする場合でおよそ10~15分程度かかる.気長に待つ.
標準出力に出力されるログを見ているといくつかエラーが表示されることがあるが,Ansibleの"PLAY RECAP"にエラーがなければ,構築自体は問題ないと思ってよい.
kubectlをインストールする
Adminサーバからkubectlを介してクラスタを操作できるようにしたい.
インストールの過程でkubectlとその設定ファイル(kubeconfig)がダウンロードされているので,これを配置することで,支障なくkubectlを利用できるようにする.
[user@k8s-admin]
cd ~
# kubectlとkubeconfigの確認
ls -l ~/kubespray/inventory/mycluster/artifacts/{kubectl,admin.conf}
# kubectl: KubernetesのCLI.実行パーミッションも付与されているはず
# admin.conf: 今回構築したクラスタに接続するためのkubeconfig
sudo cp -ip ~/kubespray/inventory/mycluster/artifacts/kubectl /usr/local/bin/kubectl
# PATHの通っているディレクトリにkubectlのバイナリをコピー
# クラスタに接続するためのkubeconfigを設定
mkdir ~/.kube
cp -ip ~/kubespray/inventory/mycluster/artifacts/admin.conf ~/.kube/config
# ~/.kube/configはkubectlが読みにいくkubeconfigのデフォルトのファイルパス
# 既存のkubeconfigを汚したくない場合は,環境変数KUBECONFIGに上記のパスを設定するとよい
# kubectlの設定を確認する
kubectl config view
# 現在のクラスタ情報を確認する
kubectl cluster-info
# Kubernetes master is running at https://10.128.0.20:6443
# coredns is running at https://10.128.0.20:6443/api/v1/namespaces/kube-system/services/coredns:dns/proxy
# kubernetes-dashboard is running at https://10.128.0.20:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy
# のような表示がされればOK
# ノードの状態を確認する
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# k8s-master1 Ready master 28h v1.15.3
# k8s-master2 Ready master 28h v1.15.3
# k8s-node1 Ready <none> 28h v1.15.3
# k8s-node2 Ready <none> 28h v1.15.3
# k8s-node3 Ready <none> 28h v1.15.3
# のような表示がされればOK
ここまでがうまくいけば,問題なくKubernetesがインストールされていることになる(正確に言うと,まだネットワークまわりの設定が確認できていない.Network Checker Applicationを参照すること).
構築ができているか検証する
試しに,今回構築したクラスタにNginxのPodをデプロイしてみる.
[user@k8s-admin]
# NginxのPodが含まれるDeploymentを登録
kubectl apply -f https://k8s.io/examples/application/deployment.yaml
# Deploymentが作成されていることを確認
kubectl describe deployment nginx-deployment
# Serviceを作成/クラスタ外部に露出し,クラスタ外部からクラスタ内のPodにアクセスできるようにする
kubectl expose deployment/nginx-deployment --type="NodePort" --port=80
# Serviceが作成されていることを確認
kubectl describe services nginx-deployment
# Type: NodePortでServiceを露出した場合,ランダムに割り当てられるポート番号(= NodePort)で,クラスタ外部からPodへのアクセスが可能になる
# "NodePort"の値を控えておく
# NodePortを介して,クラスタ外部からクラスタ内のPodにアクセスする
curl -v http://${INTERNAL_IP}:${NODE_PORT}/
# ${INTERNAL_IP}の部分は,Workerノードの内部IP(いずれか)を入力する
# ${NODE_PORT}の部分は,上で控えた"NodePort"の値を入力する
# Nginxのデフォルトテンプレートがレスポンスとして返ってくればOK
他に調べてみたこと
Network Checker Application
inventory/mycluster/group_vars/k8s-cluster/k8s-cluster.ymlのdeploy_netchecker
の値を"true"に変更することで,KubesprayがKubernetesクラスタ構築時にネットワーク診断用のPod/Serviceを自動でデプロイするようになる.
https://quay.io/repository/l23network/k8s-netchecker-agent?tag=latest&tab=tags
https://quay.io/repository/l23network/k8s-netchecker-server?tag=latest&tab=tags
[user@k8s-admin]
# Playbookの実行後
kubectl describe services netchecker-service
# netchecker-serviceがtype: NodePortで作成されていることを確認
# NodePortの値を控えておく
kubectl get pods | grep netchecker
# netchecker-agent-*, netchecker-agent-hostnet-*というPodが各ノード1つずつ,netchecker-server-*というPodがクラスタに1つ作成されていることを確認
[user@k8s-master[1-2], k8s-node[1-3]]
# Pod間ネットワークとクラスタ内部のDNSの状態を診断する
curl -v http://${INTERNAL_IP}:${NODE_PORT}/api/v1/connectivity_check
# ${INTERNAL_IP}の部分は,Workerノードの内部IP(いずれか)を入力する
# ${NODE_PORT}の部分は,上で控えた"NodePort"の値を入力する
# "{"Message":"All * pods successfully reported back to the server","Absent":null,"Outdated":null}"が表示されればOK
前述のファイアウォールの問題で悩んでいたとき,原因の切り分けに便利だった.
参考記事
- Setting up a Kubernetes cluster with Kubespray: Kubesprayによる構築については,基本的にこの記事の流れに沿っている.
お借りしたもの
- Google Cloud Logo: https://cloud.google.com/press/
- Kubernetes Logo: https://github.com/kubernetes/community/tree/master/icons
- さくらのアイコンセット(SAKURA Internet Inc.): https://knowledge.sakura.ad.jp/4724/