尝试使用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分空けておきました。

这次的目标达成

ソフトバージョン備考CentOS7.6
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で動作を確認しました。
广告
将在 10 秒后关闭
bannerAds