クラスタのデプロイ・管理が大変なのでほんとはk8sのマネージドサービス使いたい...GKEとか。
でもオンプレミスで運用せざるを得ないことってありますよね。しかもプロキシ環境下で。
そんな状況下でk8sクラスタを構築する方法の備忘録です。
kubeadm
公式が提供しているk8sクラスタを構築するためのツールです。現在のバージョンはまだBeta版ですが、2018年内にGA予定のようです。
この記事の内容は基本的には公式ドキュメントの焼き直し+α(プロキシ設定など)です。
Beta版ということもあり、クラスタデータを保持するetcdの冗長化、およびMasterコンポーネントが複数存在するようなHigh Availability構成のクラスタは単純にkubeadmを使うだけでは構築できません。
今回はHA構成までは考えず、今回利用したkubeadm v1.9がカバーする範囲である単一Master構成のクラスタを構築します。
このあたりはGA時点では対応されるんでしょうか。
環境
- CentOS 7.3(master, worker)
- プロキシ経由でインターネットに接続可能な環境
k8sクラスタ構成
- master: 単一構成
- podネットワーク: flannel
- コンテナランタイム: docker
バージョン
- kubernetes v1.9.0
- kubeadm v1.9.0
- docker v1.12.6
- flannel v0.9.1
準備
kubeadmでk8sクラスタを構築するための準備です。各master/worker nodeすべてで実施します。rootで実施する前提です。
/etc/hostsの設定
以下のように新しい行にホスト名に対応するIPアドレスを追記します。
[ip_address] [hostname]
以下のコマンド実行で追記できます(単一NIC、イーサネットの前提です)。
echo $(ip a | grep 'en\|eth0' | grep "inet" | cut -d' ' -f6 | cut -d/ -f1) $(hostname) >> /etc/hosts
yumのプロキシ設定
kubeadmはじめdocker, kubeletなどのk8sの動作に必要なパッケージはyumでインストールします。
プロキシ経由でyumが利用できるよう/etc/yum.confに以下を設定しておきます。
proxy = http://[proxy_host]:[proxy_port]
以下のコマンド実行で設定できます。
echo proxy = http://[proxy_host]:[proxy_port] >> /etc/yum.conf
-
プロキシで認証が必要な場合は以下も追加します。
echo proxy_username = [proxy_user] >> /etc/yum.conf echo proxy_password = [proxy_pass] >> /etc/yum.conf
firewall無効化
systemctl disable firewalld
systemctl stop firewalld
SELinux無効化
setenforce 0
/etc/selinux/configを編集しdisableにしておきます。
SELINUX=disabled
以下のコマンドでdisableに変更できます。
sed -e "/^SELINUX=enforcing$/s/SELINUX=enforcing/SELINUX=disabled/" -i.bak /etc/selinux/config
MACアドレスおよびproduct_uuidがすべてのノード間で重複していないことの確認
公式ドキュメントに
Unique hostname, MAC address, and product_uuid for every node
とあるので確認しておきます。
-
MACアドレス確認
ip link
-
product_uuid確認
cat /sys/class/dmi/id/product_uuid
swapを無効化
これも公式ドキュメントに
Swap disabled. You MUST disable swap in order for the kubelet to work properly.
とあるので無効にしておきます。
swapoff -a
sed -e "/^UUID=[a-z0-9-]* swap/s/^/# /" -i.bak /etc/fstab
cat /etc/fstab
Kubernetesコンポーネントが使用するポートが他プロセスで使用されていないか確認
k8sの各コンポーネントが利用するポートが使用されていないことを確認します。
-
Master node
Protocol Direction Port Range Purpose TCP Inbound 6443* Kubernetes API server TCP Inbound 2379-2380 etcd server client API TCP Inbound 10250 Kubelet API TCP Inbound 10251 kube-scheduler TCP Inbound 10252 kube-controller-manager TCP Inbound 10255 Read-only Kubelet API -
Worker nodes
Protocol Direction Port Range Purpose TCP Inbound 10250 Kubelet API TCP Inbound 10255 Read-only Kubelet API TCP Inbound 30000-32767 NodePort Services*
*部分は設定により変更可能です。今回はk8s master/node上でk8s管理外のアプリケーションプロセスが動作することは想定していないため、デフォルト設定とします。
docker v1.12.6インストール
Kubernetesではv1.12が推奨(1.13や17.03でも動作確認済みとのこと)されているため、 v1.12.6をインストールします。
dockerインストール手順もほぼ公式ドキュメントの焼き直しです。
必要なyumリポジトリの有効化
yum update -y
yum install -y yum-utils
yum-config-manager --add-repo https://docs.docker.com/v1.13/engine/installation/linux/repo_files/centos/docker.repo
yum makecache fast
dockerパッケージのインストール
yum install -y docker-engine-1.12.6-1.el7.centos
dockerのproxy設定
Docker Hubなどインターネット上のコンテナイメージリポジトリからイメージを取得するため、プロキシ経由で接続可能な設定をする必要があります。
mkdir -p /etc/systemd/system/docker.service.d
cat <<EOF > /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://[proxy_user:proxy_pass@][proxy_host]:[proxy_port]" "HTTPS_PROXY=http://[proxy_user:proxy_pass@][proxy_host]:[proxy_port]" "NO_PROXY=localhost,127.0.0.1,[master_node_ip],[worker_nodes_ip],10.96.0.0/12,10.244.0.0/16"
EOF
NO_PROXYにはすべてのk8s master/worker nodeのIPアドレスを指定します。また、k8sの各Serviceに割り振られる仮想IP(10.96.0.0/12)、および後述するpodネットワークプラグインが展開するOverlayネットワークのIPアドレス範囲(10.244.0.0/16)をCIDRで記載しておきます。
これをしないとkubeadmでクラスタ構築するときに、preflightチェックで異なるホスト間のPodおよびServiceの通信がプロキシ経由でルーティングされちゃうよ!という旨のWarningが出力され、実際にうまく通信してくれませんでした。
proxy設定の反映
systemctl daemon-reload
dockerを起動&自動起動設定
systemctl enable docker && systemctl start docker
proxy設定が反映されていることを確認
$ docker info | grep Proxy
Http Proxy: http://[proxy_host]:[proxy_port]
Https Proxy: http://[proxy_host]:[proxy_port]
No Proxy: localhost,127.0.0.1,[master_node_ip],[worker_nodes_ip],10.96.0.0/12,10.244.0.0/16
dockerの動作確認
docker単体で動作可能なことを確認します。確認できたらもう利用しないのでイメージは消しておきます。
docker run --rm hello-world
docker rmi docker.io/library/hello-world
一般ユーザからdockerを利用可能にする
groupadd docker
usermod -aG docker [user_name]
systemctl restart docker
ログアウト, 再ログインを実施し設定を反映します。
ここまででdockerのインストールが完了しました。
dockerはコンテナイメージの取得、コンテナの起動、削除をこれから構築するk8sクラスタからの指示で行うことになります。
本番環境で利用する場合の設定(ストレージドライバ)
CentOS/RHELではdockerコンテナ/イメージごとのストレージを変更差分ごとに管理するため、デフォルトでdevicemapperというストレージドライバを利用します。
devicemapperはデフォルト設定の場合loop-lvm
というモードで動作しています。このモードはコンテナ利用時のディスクI/Oのパフォーマンスが良くないので、プロダクション環境で運用する場合はdirect-lvm
モードに変更することを公式が推奨しています。
詳細は公式ドキュメントに記載がありますが、ホストマシン上にLVMとthin poolを作成する必要があります。docker v17.06以降だとdaemon.jsonに設定を記載すると自動で作ってくれるみたいですね。
kubeadmインストール、kubenetesクラスタの構築
kubadmのプロキシ設定
設定内容はdockerのプロキシ設定時と同様です。ホスト上の環境変数として指定します。
環境変数が常に有効となるように.bash_profile
に追記し反映します。
cat <<EOF >> ~/.bash_profile
PROXY_PORT=[proxy_port]
PROXY_HOST=[proxy_host]
http_proxy=http://[proxy_user:proxy_pass@]\$PROXY_HOST:\$PROXY_PORT
HTTP_PROXY=\$http_proxy
https_proxy=\$http_proxy
HTTPS_PROXY=\$http_proxy
no_proxy="localhost,127.0.0.1,[master_node_ip],[worker_nodes_ip],10.96.0.0/12,10.244.0.0/16"
EOF
source ~/.bash_profile
kubeadm, kubelet, kubectlパッケージリポジトリを追加
kubeadmとk8sの動作に必要なパッケージをインストールするためのyumリポジトリを追加します。
以下、簡単な説明です。下記以外のk8sコンポーネントはkubeadmがコンテナとして起動してくれます。
- kubelet: pod, コンテナを起動する役割を持つ、クラスタ内のすべてのホストマシンに存在するコンポーネント
- kubectl: K8sクラスタと対話するためのCLIユーティリティ
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
kubeadm, kubelet, kubectlパッケージインストール(v1.9.0)
yum install -y kubelet-1.9.0-0 kubeadm-1.9.0-0 kubectl-1.9.0-0
K8sの通信が正しくルーティングされるよう設定
k8sはiptablesを更新することでクラスタ内の各Pod間のルーティングを実現しています。
RHEL/CentOSの場合以下の設定をしないと通信が正しくルーティングされないようです。
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system
kubeletを一度起動し、Cgroup Driverがdockerとkubelet間で一致していることを確認
CentOSデフォルトだとkubeletはsystemd
、Dockerはcgroupfs
を使うはずなので、利用するドライバの指定はDockerインストール時に先に実施しておいてもいいです。
systemctl enable kubelet && systemctl start kubelet
cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf | grep KUBELET_CGROUP_ARG
docker info | grep Cgroup
systemctl stop kubelet
どちらもcgroup-driver=systemdとなっていればOK
-
異なる場合
以下を実施後再度確認します。cat << EOF > /etc/docker/daemon.json { "exec-opts": ["native.cgroupdriver=systemd"] } EOF" systemctl restart docker
masterでのみ実施する作業
masterの初期化
まずはdry-runしてみる
--kubernetes-version
でk8sのバージョンを指定しています。また、--apiserver-advertise-address
でmasterのIPアドレスを指定します。
kubeadmはworker node追加時の認証のためにトークンを払い出すのですが、利用期限が決まっておりデフォルトで24時間有効です。今回は検証用途であり、あとでworker nodeを追加したりする可能性があるため、--token-ttl 0
を指定し認証トークンの利用期限を無期限にしています。
--pod-network-cidr
にはプロキシ設定でも指定したOverlayネットワークのIPアドレス範囲(10.244.0.0/16)をCIDR形式で指定します。
kubeadm init --kubernetes-version 1.9.0 --apiserver-advertise-address=$(ip a | grep 'en\|eth0' | grep "inet" | cut -d' ' -f6 | cut -d/ -f1) --pod-network-cidr=10.244.0.0/16 --token-ttl 0 --dry-run
以下が出力されれば問題なく実行できているので、実際に初期化実行します。
[dryrun]?Finished dry-running successfully. Above are the resources that would be created.
初期化
kubeadm init --kubernetes-version 1.9.0 --apiserver-advertise-address=$(ip a | grep 'en\|eth0' | grep "inet" | cut -d' ' -f6 | cut -d/ -f1) --pod-network-cidr=10.244.0.0/16 --token-ttl 0
以下が出力されれば初期化に成功しています。kubeadm join
以降の出力はworker nodeを追加する際に実行するコマンドです。
Your Kubernetes master has initialized successfully!
To start using your cluster, you need to run (as a regular user):
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
http://kubernetes.io/docs/admin/addons/
You can now join any number of machines by running the following on each node
as root:
kubeadm join --token [token] [K8s_master_host]:[port] --discovery-token-ca-cert-hash sha256:[hash]
kube-apiserverと対話可能にする設定
初期化時に出力されていた以下のコマンドを実行します。
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Podネットワークアドオンのデプロイ
kubeadmはOverlayネットワークを展開するところまでは面倒見てくれないので、別途Podネットワークプラグインをクラスタにデプロイします。これをしないとずっとmasterのステータスがNot Ready
状態となりk8sが機能しません。
今回はflannelを利用します。
master初期化時の--pod-network-cidr
やプロキシ設定で指定したIPアドレス範囲(10.244.0.0/16)はこのflannelが展開するOverlayネットワークで利用します。
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml
master上でkube-dns PodがRunningになっていることを確認
Podネットワークが動作しているとしばらくしてkube-dnsがRunning状態になるはずなので確認します。
$ kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
etcd-[k8s_master_host] 1/1 Running 0 10m
kube-apiserver-[k8s_master_host] 1/1 Running 0 10m
kube-controller-manager-[k8s_master_host] 1/1 Running 0 10m
kube-dns-6f4fd4bdf-b7kw9 3/3 Running 0 10m
kube-flannel-ds-c2r9b 1/1 Running 0 10m
kube-proxy-s9krr 1/1 Running 0 10m
kube-scheduler-[k8s_master_host] 1/1 Running 0 10m
masterのstatusがReadyになっていることを確認
masterの状態がReady
となり、k8sクラスタとして利用できる状態になっていることを確認します。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
[k8s_master_host] Ready master 10m v1.9.0
masterでもPod起動がスケジュールされるようにmasterへのPod配置を制限するtaintを削除
デフォルトではmasterへPodが配置されないよう、taintという機能によりmasterへのPodスケジューリングが制限されています。
リソースが不足していたりmaster単体で動かす場合は、master nodeに設定されているtaintを削除します。
kubectl taint nodes --all node-role.kubernetes.io/master-
worker nodeでのみ実施する作業
worker nodeの追加
masterの初期化時に出力されたtoken, hash値を引数に指定します。
kubeadm join --token [token] [K8s_master_host]:6443 --discovery-token-ca-cert-hash sha256:[hash]
以下が出れば成功
Node join complete:
* Certificate signing request sent to master and response
received.
* Kubelet informed of new secure connection details.
Run 'kubectl get nodes' on the master to see this machine join.
K8sクラスタにworker nodeが追加されReady
になっていることをmasterで確認
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
[k8s_master_host] Ready master 10m v1.9.0
[k8s_worker_host] Ready master 10m v1.9.0
以上でオンプレミス、プロキシ環境下のホスト上でk8sクラスタを動作させることができました。
これでプロキシ環境下でもkubectl applyやhelmなどを利用してアプリケーションをクラスタ上に展開していくことができます。