尝试使用Vagrant + KVM来构建高可用的Kubernetes集群
去年之前我們使用本地OpenShift部署,輕鬆運行GitLab和Jenkins等開發工具。相比於直接使用Kubernetes,我們認為使用HA Proxy作為路由器功能的組合更易於搭建和運營。
然而,Kubernetes的进展很快,OpenShift使用的Kubernetes版本有点过时,或者遇到一些小问题,需要查阅OpenShift文档也很麻烦,因此,这次我决定尝试使用日本语书籍丰富的Kubernetes来构建。
-
- 構築手順は主に以下を参考にしました。
[書籍]Kubernetes実践ガイド
kubeadmドキュメント
kubeadmのインストール
kubeadmを使用した高可用性クラスターの作成
なお、以下は、HAクラスタとして起動してhello worldアプリケーションの動作を確認したレベルの構築手順です。まだOpenShiftのルータ機能相当や、永続化ストレージも用意していないので、今後、大きな落とし穴がある可能性があるので、注意してください。
(2019/8/26追記)ルータや永続化の対応するなかで、まだ勘違いしているかもしませんが、おおまかに以下の見直しを行いました。
仮想サーバにはデフォルトディスクサイズが40Gだったので、ストレージに追加はやめました。
vagrant-libvirtが使用する管理ネットワークとか、ingressにMetalLBに割り当てるアドレスプールとか、Heketiは固定IPアドレスしか設定できないとかあって、固定IPを割り当てるようにしました。
永続化にGlusterFSを使用することにし、物理ホスト構築時にディスク領域を50G分空けておきました。
这次的目标达成
Kubernetes1.15
Docker18.09Kubernetes 1.15がサポートしている最新バージョン。
建立物理主机
まずCentOS 7.6をVirtualization Hostでインストール。
次に仮想サーバを構築するにあたり、、、
-
- Virtual Boxは手軽そうだが性能的なオーバヘッドが大きそう。
-
- VMwareはアカウント作成とかがいろいろと面倒。
- ということで、最近はあまり記事を見かけないけど、そこは不具合は枯れている捉えて、性能も良さそうなKVMを選択しました。
で、これ以降の作業でトライ&エラーを繰り返すことが想定されることから、リトライ時間の短縮を図るため、Vagrant + KVMで構築することにしました。
VagrantとKVM連携プラグインのインストール
# yum install -y qemu-kvm libvirt libvirt-devel gcc patch
# systemctl start libvirtd
# systemctl enable libvirtd
# yum install -y https://releases.hashicorp.com/vagrant/2.2.5/vagrant_2.2.5_x86_64.rpm
# vagrant plugin install vagrant-libvirt
仮想サーバを作成するスクリプト
Vagrant.configure(2) do |config|
config.ssh.insert_key = false
$nodes = [
{ name: "k8s-master1", cpu: "2", memory: "4096", ip: "192.168.122.2"},
{ name: "k8s-master2", cpu: "2", memory: "4096", ip: "192.168.122.3"},
{ name: "k8s-worker1", cpu: "6", memory: "8192", ip: "192.168.122.10"},
{ name: "k8s-worker2", cpu: "6", memory: "8192", ip: "192.168.122.11"}
]
$nodes.each do |param|
config.vm.define param[:name] do |node|
node.vm.box = "centos/7"
node.vm.hostname = param[:name]
node.vm.provider :libvirt do |v|
v.cpus = param[:cpu]
v.memory = param[:memory]
end
node.vm.network :private_network, ip: param[:ip], :libvirt__network_name => "default"
node.vm.provision :shell do |s|
ssh_insecure_key = File.read("#{Dir.home}/.vagrant.d/insecure_private_key")
s.inline = <<-SHELL
mkdir -p /root/.ssh
echo "#{ssh_insecure_key}" >> /root/.ssh/id_rsa
chmod 400 /root/.ssh/id_rsa
cp /home/vagrant/.ssh/authorized_keys /root/.ssh/authorized_keys
echo "#{ssh_insecure_key}" >> /home/vagrant/.ssh/id_rsa
chown vagrant /home/vagrant/.ssh/id_rsa
chmod 400 /home/vagrant/.ssh/id_rsa
SHELL
end
node.vm.provision :shell, inline: $script
end
end
end
$script = <<-SCRIPT
# スワップの無効化, ブリッジでのIPv4/IPv6トラフィックを有効
swapoff -a
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
vm.swappiness = 0
EOF
sysctl --system
# SELinuxの無効化
setenforce 0
sed -i 's/^SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config
# dockerインストール
yum remove docker docker-client docker-client-latest docker-common \
docker-latest docker-latest-logrotate docker-logrotate docker-engine
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum makecache fast
yum update -y
yum install -y docker-ce-18.09.8-3.el7.x86_64 docker-ce-cli-18.09.8-3.el7.x86_64 containerd.io
mkdir -p /etc/docker
cat <<EOF > /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"storage-opts": ["overlay2.override_kernel_check=true"]
}
EOF
systemctl enable docker
systemctl daemon-reload
systemctl start docker
# kubectl, kubelet, kubeadmインストール
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
exclude=kube*
EOF
yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
# GlusterFSクライアントのインストール
yum install -y centos-release-gluster
yum install -y glusterfs-fuse
SCRIPT
-
- 仮想サーバのCPU, メモリ, ディスクサイズは適当な値です。
-
- Vagrantで構築した仮想サーバは、パスワード認証が無効化されており、秘密鍵を使ったssh接続しかできない。Kubernetesの構築手順では各ノード間をsshで接続できる必要があるそうなので、パスワード認証を有効にするとか各仮想サーバの秘密鍵を交換するかしなければならない。
- そこで「config.ssh.insert_key = false」により仮想サーバ毎に秘密鍵を生成されるのを無効化し、Vagrantが仮想サーバ構築時に使用している物理ホスト上の秘密鍵(${HOME}/.vagrant.d/insecure_private_key)を、各仮想サーバの秘密鍵としてしまう。各仮想サーバに公開鍵は登録されているので、これにより各仮想サーバ同士でssh接続が可能になる。
仮想サーバを作成
# vagrant up
また、仮想サーバで使用しているDNSを物理ホストのresolve.confに登録。
(2019/8/26追記)仮想サーバのアドレスを物理ホストのhostsファイルに追記。
# cat <<EOF >> /etc/hosts
192.168.122.2 k8s-master1
192.168.122.3 k8s-master2
192.168.122.10 k8s-worker1
192.168.122.11 k8s-worker2
EOF
これで物理ホストから仮想サーバの名前解決ができるようになる。
使用kube-apiserver创建一个负载均衡器。
# cat <<EOF > /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/\$basearch/
gpgcheck=0
enabled=1
EOF
# yum install -y nginx
# yum install -y policycoreutils-python
# semanage port -a -t http_port_t -p tcp 6443
# firewall-cmd --add-port=6443/tcp --zone=public --permanent
# firewall-cmd --reload
# cat <<EOF >> /etc/nginx/nginx.conf
stream {
upstream backend {
least_conn;
server k8s-master1:6443;
server k8s-master2:6443;
}
server {
listen 6443;
proxy_pass backend;
}
}
EOF
# systemctl enable nginx
# systemctl start nginx
-
- nginxに慣れてなくて、ここは比較的ハマりました。Kubernetes実践ガイドにある設定例を真似して/etc/nginx/conf.d配下に記述したのですが、これだとデフォルトのnginx.confだとhttpの設定として扱われてしまうんですね。そこでnginx.confで直接stream {} で括って記述しました。
- 物理ホストですがselinuxやfirewalldを無効化してません。なのでkube-apiserverと同じポートを使えるようにしています。
構築第一個主節點
现在我们要登录到主节点1并开始工作。
Kubernetes的初始配置
CNIはcalicoを使うとして、calicoのデフォルトネットワークである192.168.0.0/16だとKVM側(192.168..121)と被ってしまうので、calico側を変更しました。
# cat <<EOF > kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
kubernetesVersion: stable
apiServer:
certSANs:
- "api.kubernetes.local"
controlPlaneEndpoint: "api.kubernetes.local:6443"
networking:
podSubnet: "192.168.128.0/17"
EOF
# kubeadm init --config=kubeadm-config.yaml
... 中略 ...
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following 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:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:
kubeadm join api.kubernetes.local:6443 --token nogauk.ges57vw6ilmj8k9t \
--discovery-token-ca-cert-hash sha256:c40c63e6c1b973c6918506b9bf7a6dd76d38d0a50ea2f9106ca9b9459c754159 \
--control-plane
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join api.kubernetes.local:6443 --token nogauk.ges57vw6ilmj8k9t \
--discovery-token-ca-cert-hash sha256:c40c63e6c1b973c6918506b9bf7a6dd76d38d0a50ea2f9106ca9b9459c754159
# mkdir -p $HOME/.kube
# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
# sudo chown $(id -u):$(id -g) $HOME/.kube/config
# curl -L -O https://docs.projectcalico.org/v3.8/manifests/calico.yaml
# sed -i 's:192.168.0.0/16:192.168.128.0/17:g' ./calico.yaml
# kubectl apply -f ./calico.yaml
-
- kubeadm-config.yamlに指定したapi.kubernetes.localは物理ホストのホスト名。podSubnetがcalicoに使ってもらうネットワーク。
-
- kubeadm initが成功すると、クラスタを利用するための初期手順、マスタノードをクラスタに追加するコマンド、ワーカーノードを追加するコマンドが表示されるのでメモしておく。
- 初期手順を実行したら、calicoのマニフェストをダウンロードして、calicoが使うネットワークを修正して適用。
将主节点1的证书复制到主节点2上。
在Vagrantfile中,我已经复制了私钥,但那是为了vagrant用户而言的,但是vagrant用户无法访问证书。因此,我将私钥复制给root用户,然后执行证书的分发。
# cp /home/vagrant/.ssh/id_rsa ~/.ssh
# USER=vagrant
# CONTROL_PLANE_IPS="k8s-master2"
# for host in ${CONTROL_PLANE_IPS}; do
sudo scp /etc/kubernetes/pki/ca.crt ${USER}@$host:;
sudo scp /etc/kubernetes/pki/ca.key ${USER}@$host:;
sudo scp /etc/kubernetes/pki/sa.key ${USER}@$host:;
sudo scp /etc/kubernetes/pki/sa.pub ${USER}@$host:;
sudo scp /etc/kubernetes/pki/front-proxy-ca.crt ${USER}@$host:;
sudo scp /etc/kubernetes/pki/front-proxy-ca.key ${USER}@$host:;
sudo scp /etc/kubernetes/pki/etcd/ca.crt ${USER}@$host:etcd-ca.crt;
sudo scp /etc/kubernetes/pki/etcd/ca.key ${USER}@$host:etcd-ca.key;
sudo scp /etc/kubernetes/admin.conf ${USER}@$host:;
done
マスタノード2の構築
然后登录到主节点2,并使用root用户执行以下操作。
证书的应用
# USER=vagrant
# mkdir -p /etc/kubernetes/pki/etcd
# mv /home/${USER}/ca.crt /etc/kubernetes/pki/
# mv /home/${USER}/ca.key /etc/kubernetes/pki/
# mv /home/${USER}/sa.pub /etc/kubernetes/pki/
# mv /home/${USER}/sa.key /etc/kubernetes/pki/
# mv /home/${USER}/front-proxy-ca.crt /etc/kubernetes/pki/
# mv /home/${USER}/front-proxy-ca.key /etc/kubernetes/pki/
# mv /home/${USER}/etcd-ca.crt /etc/kubernetes/pki/etcd/ca.crt
# mv /home/${USER}/etcd-ca.key /etc/kubernetes/pki/etcd/ca.key
# mv /home/${USER}/admin.conf /etc/kubernetes/admin.conf
加入集群中的 Master 节点 2.
# kubeadm join api.kubernetes.local:6443 --token nogauk.ges57vw6ilmj8k9t \
--discovery-token-ca-cert-hash sha256:c40c63e6c1b973c6918506b9bf7a6dd76d38d0a50ea2f9106ca9b9459c754159 \
--control-plane
※手元のログだと–control-planeだけど、ドキュメントとかだと–experimental-control-planeなので、別途、要確認。
* (2019/8/26追記)ドキュメントだと–experimental-control-planeだけど、kubeadm initで出力されるログには –control-plane とあるので、それを使用。
构建工作节点
在各个工作节点上使用root用户登录,并执行以下操作。
ワーカノードをクラスタに参加
# kubeadm join api.kubernetes.local:6443 --token nogauk.ges57vw6ilmj8k9t --discovery-token-ca-cert-hash sha256:c40c63e6c1b973c6918506b9bf7a6dd76d38d0a50ea2f9106ca9b9459c754159
你好,世界应用程序正在运行。
在主节点1的root用户下执行以下操作。
# kubectl create deployment hello-node --image=gcr.io/hello-minikube-zero-install/hello-node
# kubectl expose deployment hello-node --type=LoadBalancer --port=8080
# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-node LoadBalancer 10.102.47.188 <pending> 8080:32223/TCP 3h38m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 40h
# kubectl get pod
NAME READY STATUS RESTARTS AGE
hello-node-55b49fb9f8-xfxgk 1/1 Running 0 3h38m
# kubectl get po/hello-node-55b49fb9f8-xfxgk -o jsonpath='{.status.hostIP}'
192.168.121.208
# kubectl get po/hello-node -o jsonpath='{.spec.ports[0].nodePort}'
32223
# curl http://192.168.121.208:32223
Hello World!
-
- kubectl createでhello worldアプリケーションをデプロイ。
- kubectl exposeでサービスを設定。ingressとかまだだから、EXTERNAL-IPはpending。なのでワーカーノードに直接curlで動作を確認しました。