Edited at

kubeadmを使ってオンプレミスのプロキシ環境下にKubernetesクラスタをデプロイする(CentOS 7.3)

More than 1 year has passed since last update.

クラスタのデプロイ・管理が大変なのでほんとは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などを利用してアプリケーションをクラスタ上に展開していくことができます。


参考サイト