kubeadmを用いたKubernetes HAクラスタ on AWS
AWS上でkubeadm
を使い、Kubernetes HAクラスタを作ってみたときのメモです。
構成
前提
- 作業ユーザは
root
手順
Dockerのインストール(aws-k8s-master[01-03]、aws-k8s-node[01-03])
yum install -y docker systemctl enable docker && systemctl start docker
kubeadm、kubelet、kubectlのインストール(aws-k8s-master[01-03]、aws-k8s-node[01-03])
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 sed -i 's/^SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config setenforce 0 yum install -y kubelet kubeadm kubectl systemctl enable kubelet && systemctl start kubelet
cat <<EOF > /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system
cfssl、cfssljsonのインストール(aws-k8s-master[01-03])
curl -o /usr/local/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 curl -o /usr/local/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 chmod +x /usr/local/bin/cfssl*
etcdサーバのためのCA証明書作成(aws-k8s-master01)
mkdir -p /etc/etcd/pki cd /etc/etcd/pki cat >ca-config.json <<EOF { "signing": { "default": { "expiry": "175200h" }, "profiles": { "server": { "expiry": "175200h", "usages": [ "signing", "key encipherment", "server auth", "client auth" ] }, "client": { "expiry": "175200h", "usages": [ "signing", "key encipherment", "client auth" ] }, "peer": { "expiry": "175200h", "usages": [ "signing", "key encipherment", "server auth", "client auth" ] } } } } EOF # 証明書の有効期限は20年
cat >ca-csr.json <<EOF { "CN": "etcd", "key": { "algo": "rsa", "size": 2048 } } EOF
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
etcdのクライアント証明書の作成(aws-k8s-master01)
cat >client.json <<EOF { "CN": "client", "key": { "algo": "ecdsa", "size": 256 } } EOF
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client.json | cfssljson -bare client
環境変数のエクスポート(aws-k8s-master[01-03])
export PEER_NAME=$(hostname) export PRIVATE_IP=$(ip addr show eth0 | grep -Po 'inet \K[\d.]+')
SSHキーペアの作成(aws-k8s-master02, aws-k8s-master03)
ssh-keygen -t rsa -b 4096 -C ""
cat ~/.ssh/id_rsa.pub # 出力された内容をaws-k8s-master01の/root/.ssh/authorized_keysのエントリを追加する
CA証明書とクライアント証明書の配布(aws-k8s-master02, aws-k8s-master03)
mkdir -p /etc/etcd/pki cd /etc/etcd/pki scp root@<aws-k8s-master01のIP>:/etc/etcd/pki/ca.pem . scp root@<aws-k8s-master01のIP>:/etc/etcd/pki/ca-key.pem . scp root@<aws-k8s-master01のIP>:/etc/etcd/pki/client.pem . scp root@<aws-k8s-master01のIP>:/etc/etcd/pki/client-key.pem . scp root@<aws-k8s-master01のIP>:/etc/etcd/pki/ca-config.json .
etcdサーバ証明書とpeer証明書の作成(aws-k8s-master[01-03])
cfssl print-defaults csr > config.json sed -i '0,/CN/{s/example\.net/'"$PEER_NAME"'/}' config.json sed -i 's/www\.example\.net/'"$PRIVATE_IP"'/' config.json sed -i 's/example\.net/'"$PEER_NAME"'/' config.json
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server config.json | cfssljson -bare server cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=peer config.json | cfssljson -bare peer
etcdサーバのsystemd登録と起動設定(aws-k8s-master[01-03])
export ETCD_VERSION="v3.1.12" curl -sSL https://github.com/coreos/etcd/releases/download/${ETCD_VERSION}/etcd-${ETCD_VERSION}-linux-amd64.tar.gz | tar -xzv --strip-components=1 -C /usr/local/bin/
touch /etc/etcd.env echo "PEER_NAME=${PEER_NAME}" >> /etc/etcd.env echo "PRIVATE_IP=${PRIVATE_IP}" >> /etc/etcd.env
cat >/etc/systemd/system/etcd.service <<EOF [Unit] Description=etcd Documentation=https://github.com/coreos/etcd Conflicts=etcd.service Conflicts=etcd2.service [Service] EnvironmentFile=/etc/etcd.env Type=notify Restart=always RestartSec=5s LimitNOFILE=40000 TimeoutStartSec=0 ExecStart=/usr/local/bin/etcd --name <name> --data-dir /var/lib/etcd --listen-client-urls https://<my_ip>:2379 --advertise-client-urls https://<my_ip>:2379 --listen-peer-urls https://<my_ip>:2380 --initial-advertise-peer-urls https://<my_ip>:2380 --cert-file=/etc/etcd/pki/server.pem --key-file=/etc/etcd/pki/server-key.pem --client-cert-auth --trusted-ca-file=/etc/etcd/pki/ca.pem --peer-cert-file=/etc/etcd/pki/peer.pem --peer-key-file=/etc/etcd/pki/peer-key.pem --peer-client-cert-auth --peer-trusted-ca-file=/etc/etcd/pki/ca.pem --initial-cluster aws-k8s-master01=https://<aws-k8s-master01のIP>:2380,aws-k8s-master02=https://<aws-k8s-master02のIP>:2380,aws-k8s-master03=https://<aws-k8s-master03のIP>:2380 --initial-cluster-token my-etcd-token --initial-cluster-state new # <name>にはaws-k8s-master[01-03]が入る [Install] WantedBy=multi-user.target EOF
systemctl daemon-reload systemctl enable etcd systemctl start etcd
NLBの作成(AWS ManagementConsole等)
(6443/TCPのリスナを作成、aws-k8s-master01のインスタンスをターゲットグループに登録する)
(NLB作成後、DNS名にpingを発行し、NLBのIPを確認する)
kubeadmの実行(aws-k8s-master01)
cat >config.yaml <<EOF apiVersion: kubeadm.k8s.io/v1alpha1 kind: MasterConfiguration etcd: endpoints: - https://<aws-k8s-master01のIP>:2379 - https://<aws-k8s-master02のIP>:2379 - https://<aws-k8s-master03のIP>:2379 caFile: /etc/etcd/pki/ca.pem certFile: /etc/etcd/pki/client.pem keyFile: /etc/etcd/pki/client-key.pem networking: podSubnet: 192.168.0.0/16 apiServerCertSANs: - <NLBのIP> - <my_ip> apiServerExtraArgs: apiserver-count: "3" EOF
kubeadm init --config=config.yaml
mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config
kubeadmの実行(aws-k8s-master02,aws-k8s-master03)
mkdir /etc/kubernetes/pki scp root@<aws-k8s-master01のIP>:/etc/kubernetes/pki/* /etc/kubernetes/pki rm -f /etc/kubernetes/pki/apiserver*
cat >config.yaml <<EOF apiVersion: kubeadm.k8s.io/v1alpha1 kind: MasterConfiguration etcd: endpoints: - https://<aws-k8s-master01のIP>:2379 - https://<aws-k8s-master02のIP>:2379 - https://<aws-k8s-master03のIP>:2379 caFile: /etc/etcd/pki/ca.pem certFile: /etc/etcd/pki/client.pem keyFile: /etc/etcd/pki/client-key.pem networking: podSubnet: 192.168.0.0/16 apiServerCertSANs: - <NLBのIP> - <my_ip> apiServerExtraArgs: apiserver-count: "3" EOF
kubeadm init --config=config.yaml
mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config
NLBへ残りのMasterを追加(AWS ManagementConsole等)
(NLBのターゲットグループにaws-k8s-master02、aws-k8s-master03を追加)
calicoのインストール(aws-k8s-master01)
kubectl apply -f https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/rbac-kdd.yaml kubectl apply -f https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml
Nodeのクラスタ参加(aws-k8s-node[01-03])
kubeadm join --token <token> <aws-k8s-master01のIP>:6443 --discovery-token-ca-cert-hash sha256:<hash> # <token>、<hash>はkubeadm initをaws-k8s-master01で実行した際に表示されたものを使用
kube-proxyの再設定(aws-k8s-master01)
kubectl get configmap -n kube-system kube-proxy -o yaml > kube-proxy-cm.yaml sed -i 's#server:.*#server: https://<NLBのIP>:6443#g' kube-proxy-cm.yaml kubectl apply -f kube-proxy-cm.yaml --force kubectl delete pod -n kube-system -l k8s-app=kube-proxy
kubeletの再設定(aws-k8s-node[01-03])
sed -i 's#server:.*#server: https://<NLBのIP>:6443#g' /etc/kubernetes/kubelet.conf systemctl restart kubelet
gVisorを試してみた
Googleが開発したgVisorを試して、簡単に性能調査してみましたので、メモとして残しておきます。
環境
gVisorのインストール手順
- Dockerのインストール
sudo apt-get update sudo apt-get install -y \ apt-transport-https \ ca-certificates \ curl \ software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" sudo apt-get update sudo apt-get install -y docker-ce
- gVisor(runsc)のインストール
wget https://storage.googleapis.com/gvisor/releases/nightly/latest/runsc chmod +x runsc sudo mv runsc /usr/local/bin sudo sh -c 'cat <<EOF > /etc/docker/daemon.json { "runtimes": { "runsc": { "path": "/usr/local/bin/runsc" } } } EOF' sudo systemctl restart docker
- コンテナを起動してみる
sudo docker run --runtime=runc hello-world sudo docker run --runtime=runsc hello-world
性能調査
簡単なスクリプトでコンテナ起動と削除のスピードを評価します。
cat <<EOF > runc-test.sh for i in \$(seq 1 \$1) ; do sudo docker run --runtime=runc --name gvisor-test hello-world > /dev/null 2>&1 sudo docker rm gvisor-test > /dev/null 2>&1 done EOF chmod +x runc-test.sh cat <<EOF > runsc-test.sh for i in \$(seq 1 \$1) ; do sudo docker run --runtime=runsc --name gvisor-test hello-world > /dev/null 2>&1 sudo docker rm gvisor-test > /dev/null 2>&1 done EOF chmod +x runsc-test.sh
スクリプトの使い方としては./runc-test.sh <起動および削除回数>
、./runsc-test.sh <起動および削除回数>
とします。試しに両方100回ずつ起動・削除を繰り返すと、以下のような結果となりました。
ubuntu@gvisor-01:~$ time ./runc-test.sh 100 real 1m20.991s user 0m14.948s sys 0m1.768s ubuntu@gvisor-01:~$ time ./runsc-test.sh 100 real 1m13.219s user 0m15.100s sys 0m1.748s
起動・停止だけではrunc
とあまり差はみられませんね。むしろrunsc
の方が速い?
ネットワークトラフィックの差をとるとrunc
に軍配が上がるみたいですが。。。
追記(2018/06/07) 気になってDisk書き込み速度も調べてみましたが、逆転現象が起きましたw
- runc
root@4aee2e622884:/# dd if=/dev/zero of=/tmp/write.tmp ibs=1M obs=1M count=1024 1024+0 records in 1024+0 records out 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 4.17698 s, 257 MB/s
- runsc
root@ce7e3926f52e:/# dd if=/dev/zero of=/tmp/write.tmp ibs=1M obs=1M count=1024 1024+0 records in 1024+0 records out 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.853382 s, 1.3 GB/s
Rancher 2.0で複数のKubernetes環境を管理してみる
2018年4月末にコンテナ管理プラットフォームであるRancherのバージョン2.0がGA(General Availability)となりました。対応するコンテナオーケストレーションツールをKubernetesに絞り、オンプレ/クラウド問わずKubernetesクラスタをマネジメントするOSSに昇華されているみたいです。今回はこのRancher 2.0を使って
というところまでをやってみたいと思います。
環境
今回は以下の環境で試用してみます。
- OS
- CentOS 7.4
- Rancher
- Ver: v2.0.2
- インポートするKubernetes
- Ver: 1.10.0
- RKE(Rancher Kubernetes Engine)のKubernetes
- Ver: 1.10.1
- 構成サーバ群
- Rancherサーバ : rancher-server-01
- RKE k8sマスター: rancher-k8s-master-01
- RKE k8sノード#1: rancher-k8s-node-01
- RKE k8sノード#2: rancher-k8s-node-02
- 自前k8sマスター: tst-k8s-master01
- 自前k8sノード#1: tst-k8s-node01
- 自前k8sノード#2: tst-k8s-node02
事前準備
今回は検証なので、ポートは全開放でいきます。以下のコマンドを全ノードで叩きます。
firewall-cmd --set-default-zone=trusted
次に全ノードにDockerをインストールして、サービスを起動します。
yum -y install docker systemctl enable docker systemctl start docker
この時点でインポートする自前k8sを構築しておきます。注意点としては、kube-dns
とIngress Controllerを忘れずにデプロイしておきます。
Rancherサーバの構築
Rancherサーバを構築していきますが、難しいことはありません。以下のコマンドを叩くだけです。
mkdir /var/lib/rancher docker run -d --restart=unless-stopped -p 80:80 -p 443:443 -v /var/lib/rancher:/var/lib/rancher:Z rancher/rancher:v2.0.2
ここで/var/lib/rancher
はRancherサーバの永続ボリュームとして使うディレクトリなので、お好みで調整してください。
またCentOSをはじめとしたRHEL系OSではSELinuxが有効になっているかと思いますので、-v
オプションの末尾に:Z
をつけて適切なラベリングを行うようにしておきます。
Rancherサーバが起動したら、ウェブブラウザからRancherサーバにログインします。するとまずadmin
ユーザのパスワードを作成するよう求められるので、パスワードを入力して次に進みます。
RancherサーバのURLはアクセスした際のURLがデフォルトで表示されていますので、そのままで次に進みます。
すると以下のような画面が表示されます。クラスタを登録する前にHelm(k8sのパッケージマネージャ)を有効化しておきましょう。「Catalog」を選択します。
「Helm Stable」のスイッチを「Enabled」にします。
これでHelmの有効化は完了です。
新しいKubernetesクラスタの作成
次にクラスタを登録していきましょう。まずはRKEクラスタを作成・登録します。「Clusters」を選択し、「Add Cluster」をクリックします。
すると以下のような画面になります。今回は一般的な仮想マシンからk8sクラスタを作成しますので、「CUSTOM」を選択し、「Next」をクリックします。
次の画面ではRKE k8sクラスタを作成するのに必要なコマンドが表示されます。k8sマスターを登録をするには「Node Role」の
- etcd
- Control
- Worker
にチェックを入れ、記載されているコマンドを実行します。が、SELinuxが有効な環境に対してはこのままではダメで、-v
オプションの末尾に:Z
をつけるのを忘れないようにしましょう。
同様にk8sノードを登録するには「Node Role」の
- Worker
にチェックを入れ、記載されているコマンドを実行します。-v
オプションの末尾に:Z
をつけるのを忘れずに。
それぞれのサーバでコマンド実行が終わったら「Done」をクリックします。そうするとRKE k8sクラスタの作成が始まります。
既存のKubernetesクラスタのインポート
RKE k8sクラスタの作成を待っている間に既存k8sクラスタをRancherサーバにインポートしましょう。「Clusters」を選択して「Add Cluster」をクリックし、今度は「IMPORT」を選択します。その後「Next」をクリックしましょう。
すると既存k8sクラスタをRancherサーバに登録するためのコマンドが発行されますので、k8sマスター上で二番目のコマンドを叩きます。叩いたら「Done」をクリックします。
戻った画面で「State」の値が「Active」になっていればRKE k8sクラスタの作成・登録、既存k8sクラスタの登録は完了です。
Helmでアプリケーションをデプロイ
Rancherに二つのk8sクラスタが登録されたので、それぞれのクラスタ上にアプリケーションをデプロイしてみましょう。まずはRKE k8sクラスタ上にデプロイしてみます。「Global」から「Cluster: rke-cluster-01 > Default」をクリックします。
「Catalog Apps」を選択し、「Launch」をクリックします。
表示されたカタログ群から「dokuwiki」を選択します。
今回はPersistent Volumeをまだサポートしていないクラスタなので、アプリケーションのデプロイでPersistent Volumeを使わない設定にします。またデフォルトではLoadBalancer
タイプのServiceが作られるので、これをClusterIP
に変更します。下のほうに進んで「Add Answer」を2回クリックし、以下のKey-Valueを入力します。その後「Launch」をクリックします。
- serviceType = ClusterIP
- persistence.enabled = false
しばらくすると以下のようにゲージが緑になります。この状態になれば必要なPodは上がっている状態です。
では、Ingressを作成して外部からdokuwikiにアクセスできるようにしましょう。「Workloads」を選択し「Load Balancing」タブを選んでから「Add Ingress」をクリックします。
Ingressを作成する際のパラメータは以下の通りにします。
- Name: (任意の名前)
- Namespace: (作成されたNamespace)
- Rules
- Automatically generate a
.xip.io
hostname - Target: (作成されたService)
- Port: 80
- Automatically generate a
以上、入力したら下の「Save」をクリックします。少し経つと以下の通りURLが発行されるので、アクセスしてみましょう。dokuwikiのホームページが表示されます!
次は既存k8sクラスタ上にアプリケーションをデプロイしてみます。「Global」から「Cluster: my-cluster-01 > Default」をクリックします。
「Catalog Apps」を選択し、「Launch」をクリックします。
表示されたカタログ群から「zeppelin」を選択します。
このカタログではデフォルトのCPU・メモリリソースを最低2000millicore、4096MiB確保するように設定されているので、今回構築したクラスタで展開できる値に修正します。「Add Answer」を2回クリックし、以下のKey-Valueを入力します。その後「Launch」をクリックします。
- zeppelin.resources.limits.cpu: 1000m
- zeppelin.resources.limits.meory: 2048Mi
ゲージが緑になるまで待ちましょう。
dokuwikiと同様、Ingressを作っていきます。「Workloads」を選択し「Load Balancing」タブを選んでから「Add Ingress」をクリックします。
Ingressを作成する際のパラメータは以下の通りにします。
- Name: (任意の名前)
- Namespace: (作成されたNamespace)
- Rules
- Specify a hostname use
- Request Host: (Ingress Controllerに対応した名前)
- Target: (作成されたService)
- Port: 8080
- Specify a hostname use
以上、入力したら下の「Save」をクリックします。表示されたURLにアクセスしてみましょう。zeppelinのホームページが表示されます!
API Aggregationを有効化してPrometheusのメトリクスからHorizonPodAutoscalerでPodのオートスケールをする
やたら長いタイトルですが…
KubernetesではAPI Aggregationといって、Custom Metrics API Serverを用意しkube-apiserver
と連携することでAPIを拡張することができます。例えば今回のタイトルにもある通り、Prometheusが取得したメトリクスをAPIから取得できるようにすることができます。この記事では以下を可能とするKubernetesの設定を説明します。
API Aggregationの有効化
API Aggregationを有効化するにはkube-apiserver
とkube-controller-manager
に設定が必要になります。
まず、kube-controller-manager
に以下のオプションを設定して再起動します。
--horizontal-pod-autoscaler-use-rest-clients=true
次にkube-apiserver
の認証方式において、x509証明書認証を有効化するために、CA証明書を用意します。CA証明書のファイルパスを/etc/kubernetes/certs/ca.crt
とします。
さらにkube-apiserver
とCustom Metrics API Serverとが連携するために使うクライアント証明書と秘密鍵、そしてクライアント証明書の署名に使ったCAのCA証明書を用意します。それぞれのファイルパスを/etc/kubernetes/certs/proxy-client.crt
、/etc/kubernetes/certs/proxy-client.key
、/etc/kubernetes/certs/proxy-ca.crt
とします。
その後kube-apiserver
に以下のオプションを設定します
--client-ca-file=/etc/kubernetes/certs/ca.crt --requestheader-client-ca-file=/etc/kubernetes/certs/proxy-ca.crt --requestheader-allowed-names='' --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --proxy-client-cert-file=/etc/kubernetes/certs/proxy-client.crt --proxy-client-key-file=/etc/kubernetes/certs/proxy-client.key
最後にkube-apiserver
の再起動を行うのですが、もし既に--client-ca-file
の値を指定していて今回CA証明書を変更する場合、既にAPIサーバ間連携に使用するConfigMap extension-apiserver-authentication
ができてしまっているので、これをあらかじめ削除しておきます。
kubectl -n kube-system delete configmap extension-apiserver-authentication
その後、kube-apiserver
を再起動します。
これでAPI Aggregationの設定は完了です。
Prometheus用のCustom Metrics API Serverの構築とkube-apiserverとの連携
Prometheus用のCustom Metrics API Serverを構築し、kube-apiserverにAPIエンドポイントを作成してkube-apiserver
とCustom Metrics API Serverとを連携させます。
前提条件として名前空間kube-system
にPrometheusは構築済みであるとします。ここではPrometheusサーバ(port: 9090)のホスト名をprometheus-server
であるとします。
まずはCustom Metrics API Serverを構築するためのKubernetes Manifestをダウンロードします。
git clone https://github.com/DirectXMan12/k8s-prometheus-adapter cd k8s-prometheus-adapter
ダウンロードしてきたManifestの中にあるPrometheusサーバの指定を変更します。
sed -i 's/prometheus.prom.svc:9090/prometheus-server/g' deploy/manifests/custom-metrics-apiserver-deployment.yaml
また、Manifest内でリソースを作成する名前空間をkube-system
に変更します。
sed -i 's/namespace: custom-metrics/namespace: kube-system/g' deploy/manifests/*
さらにPullしてくるCustom Metrics API Serverコンテナイメージのタグを付与します。
sed -i 's/directxman12\/k8s-prometheus-adapter/directxman12\/k8s-prometheus-adapter:advanced-config/g' deploy/manifests/custom-metrics-apiserver-deployment.yaml
次にCustom Metrics API Serverのサーバ証明書と秘密鍵を作成します。ファイル名はそれぞれserving.crt
、serving.key
とします。
作成したサーバ証明書と秘密鍵を使用してSecret cm-adapter-serving-certs
を作成します。
kubectl -n kube-system create secret generic --from-file=serving.crt=serving.crt --from-file=serving.key=serving.key cm-adapter-serving-certs
最後にCustom Metrics API Serverを構築します。
kubectl create -f deploy/manifests/
構築がうまくいっていれば、APIを叩くと以下のような出力が得られると思います。
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq . { "kind": "APIResourceList", "apiVersion": "v1", "groupVersion": "custom.metrics.k8s.io/v1beta1", "resources": [ { "name": "pods/fs_sector_writes", "singularName": "", "namespaced": true, "kind": "MetricValueList", "verbs": [ "get" ] }, <中略> { "name": "jobs.batch/kube_pod_status_phase", "singularName": "", "namespaced": true, "kind": "MetricValueList", "verbs": [ "get" ] } ] }
PrometheusのメトリクスからPodをオートスケールする
では実際に構築したCustom Metrics API Serverを使ってオートスケールを実現してみましょう。 まずはテスト用にDeployment、Serviceを作ります。
kubectl create deploy httpd --image httpd kubectl expose deploy httpd --port 80 kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd ClusterIP 172.16.132.62 <none> 80/TCP 10s kubernetes ClusterIP 172.16.0.1 <none> 443/TCP 56m
次にHorizontalPodAutoScalerをDeployment httpd
に対して作ります。
cat << EOF > hpa-httpd.yaml apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: httpd namespace: default spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: httpd minReplicas: 1 maxReplicas: 8 metrics: - type: Pods pods: metricName: cpu_usage targetAverageValue: 1m EOF kubectl create -f hpa-httpd.yaml
HPAが作成出来たら、以下のスクリプトを実行してPodに負荷をかけます。
cat << EOF > hpa-test.sh #!/bin/sh count=1 while(true) do curl http://172.16.132.62 > /dev/null 2>&1 & echo access count = \$((count++)) done EOF chmod 755 hpa-test.sh ./hpa-test.sh
するとPodが増えていく様が見て取れるはずです。
KubernetesでHorizontal Pod Autoscalerを使えるようにする
KubernetesにおけるHorizontal Pod Autoscaler(HPA)とは、Podの過負荷時(CPU使用量増、メモリ使用量増など)においてPodをオートスケーリングするリソースです。DeploymentやReplicaSetに紐付けて使います。
HPAを使ってオートスケーリングを実現するには、Podの負荷を監視する必要があります。つまり種々のメトリクス(CPU使用率、メモリ使用率など)を取得する必要があります。
Kubernetesでもっとも簡単にメトリクス取得する方法はおそらくHeapsterという監視ソフトウェアを使うことです。Helmを使って簡単にインストールできます。
# helm install stable/heapster --namespace kube-system
Kubernetes v.1.10だと、HPAを実現するデフォルトのメトリクス取得先はAggregation APIになっており、Heapsterのメトリクスを見てくれません。そこでkube-controller-manager
の起動オプションに次のオプションを加えます。
--horizontal-pod-autoscaler-use-rest-clients=false
これでHeapsterを使ってオートスケーリングを実現することができます。
Haproxy Ingress Controllerの導入
OpenShiftではおなじみのroute機能を、Kubernetes上でもIngressを使って再現してみたいと思います。
まずはDNSワイルドカードの有効化から。今回は*.ha-kubernetes.internal
というアドレスをhaproxyポッドに割り当てます。
DNSサーバの設定
# vi /etc/dnsmasq.conf
address=/ha-kubernetes.internal/192.168.0.89
# systemctl restart dnsmasq
例のごとくマルチマスターKubernetes環境を前提とするので、フロントエンドのロードバランサ(これもhaproxy)に対し80や443ポートにアクセスが来たら、マスターの同一ポートにリクエストを流すように設定します。
フロントエンドロードバランサの設定(haproxy-*)
# vi /etc/haproxy/haproxy.cfg
... frontend router_http *:80 default_backend router_http frontend router_https *:443 default_backend router_https frontend router_stat *:1936 default_backend router_stat ... backend router_http balance leastconn server master1 kube-master-01.bbrfkr.mydns.jp:80 check server master2 kube-master-02.bbrfkr.mydns.jp:80 check backup server master3 kube-master-03.bbrfkr.mydns.jp:80 check backup backend router_https balance leastconn server master1 kube-master-01.bbrfkr.mydns.jp:443 check server master2 kube-master-02.bbrfkr.mydns.jp:443 check backup server master3 kube-master-03.bbrfkr.mydns.jp:443 check backup backend router_stat balance leastconn server master1 kube-master-01.bbrfkr.mydns.jp:1936 check server master2 kube-master-02.bbrfkr.mydns.jp:1936 check backup server master3 kube-master-03.bbrfkr.mydns.jp:1936 check backup
# systemctl restart haproxy
デフォルトでkube-apiserverはnodePortに30000-32767の間しか指定できないので、この縛りを緩くします。
nodeportレンジの変更(kube-master-*)
# sed -i 's/KUBE_API_ARGS="/KUBE_API_ARGS="--service-node-port-range 1-32767 /g' /etc/kubernetes/apiserver # systemctl restart kube-apiserver
これからが実際のHaproxy Ingress Controllerの設定です。
haproxy ingress controllerの作成
- デフォルトページ(404)作成
# vi ingress-default-deployment.yaml
apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: run: ingress-default-backend name: ingress-default-backend namespace: kube-system spec: replicas: 1 selector: matchLabels: run: ingress-default-backend template: metadata: labels: run: ingress-default-backend spec: containers: - name: ingress-default-backend image: gcr.io/google_containers/defaultbackend:1.0 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: labels: run: ingress-default-backend name: ingress-default-backend namespace: kube-system spec: ports: - name: port-1 port: 8080 protocol: TCP targetPort: 8080 selector: run: ingress-default-backend
# kubectl create -f ingress-default-deployment.yaml
- controllerのコンフィグ作成
# vi haproxy-configmap.yaml
apiVersion: v1 data: dynamic-scaling: "true" backend-server-slots-increment: "4" kind: ConfigMap metadata: name: haproxy-configmap namespace: kube-system
# kubectl create -f haproxy-configmap.yaml
- ingress controllerのデプロイ
# vi haproxy-ingress-deployment.yaml
apiVersion: v1 kind: ServiceAccount metadata: name: haproxy-ingress namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: haproxy-ingress-clusterrole rules: - apiGroups: - "" resources: - configmaps - endpoints - nodes - pods - secrets verbs: - list - watch - apiGroups: - "" resources: - nodes verbs: - get - apiGroups: - "" resources: - services verbs: - get - list - watch - apiGroups: - "extensions" resources: - ingresses verbs: - get - list - watch - apiGroups: - "" resources: - events verbs: - create - patch - apiGroups: - "extensions" resources: - ingresses/status verbs: - update --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: Role metadata: name: haproxy-ingress-role namespace: kube-system rules: - apiGroups: - "" resources: - configmaps - pods - secrets - namespaces verbs: - get - apiGroups: - "" resources: - configmaps resourceNames: # Defaults to "<election-id>-<ingress-class>" # Here: "<ingress-controller-leader>-<haproxy>" # This has to be adapted if you change either parameter # when launching the haproxy-ingress-controller. - "ingress-controller-leader-haproxy" verbs: - get - update - apiGroups: - "" resources: - configmaps verbs: - create - apiGroups: - "" resources: - endpoints verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: RoleBinding metadata: name: haproxy-ingress-role-nisa-binding namespace: kube-system roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: haproxy-ingress-role subjects: - kind: ServiceAccount name: haproxy-ingress namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: haproxy-ingress-clusterrole-nisa-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: haproxy-ingress-clusterrole subjects: - kind: ServiceAccount name: haproxy-ingress namespace: kube-system --- apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: run: haproxy-ingress name: haproxy-ingress namespace: kube-system spec: replicas: 1 selector: matchLabels: run: haproxy-ingress template: metadata: labels: run: haproxy-ingress spec: containers: - name: haproxy-ingress image: quay.io/jcmoraisjr/haproxy-ingress args: - --default-backend-service=kube-system/ingress-default-backend - --default-ssl-certificate=kube-system/tls-secret - --configmap=$(POD_NAMESPACE)/haproxy-configmap - --reload-strategy=native ports: - name: http containerPort: 80 - name: https containerPort: 443 - name: stat containerPort: 1936 env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace serviceAccountName: haproxy-ingress
# kubectl create -f haproxy-ingress-deployment.yaml
- ingress controllerの外部公開サービス作成
# vi haproxy-ingress-svc.yaml
apiVersion: v1 kind: Service metadata: labels: run: haproxy-ingress name: haproxy-ingress namespace: kube-system spec: type: NodePort ports: - name: port-1 port: 80 protocol: TCP targetPort: 80 nodePort: 80 - name: port-2 port: 443 protocol: TCP targetPort: 443 nodePort: 443 - name: port-3 port: 1936 protocol: TCP targetPort: 1936 nodePort: 1936 selector: run: haproxy-ingress
# kubectl create -f haproxy-ingress-svc.yaml
ここまでで、Haproxy Ingress Controllerの構成はおしまいです。実際に動作確認をしてみましょう。
動作確認
- エンドポイントサービスの作成
# vi httpd-deployment.yaml
apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: run: httpd name: httpd spec: replicas: 1 selector: matchLabels: run: httpd template: metadata: labels: run: httpd spec: containers: - name: httpd image: httpd ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: labels: run: httpd name: httpd namespace: default spec: ports: - name: port-1 port: 80 protocol: TCP targetPort: 80 selector: run: httpd
# kubectl create -f httpd-deployment.yaml
- Ingressの作成
# vi httpd-ingress.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: httpd spec: rules: - host: test.ha-kubernetes.internal http: paths: - path: / backend: serviceName: httpd servicePort: 80
# kubectl create -f httpd-ingress.yaml
- ブラウザでtest.ha-kubernetes.internalにアクセス
Kubernetes環境にDashboardとDNSサービスをデプロイする
前回作成したマルチマスターKubernetes環境はKubernetesの最小構成なので、今回はDashboardとDNSサービスをデプロイしたいと思います。 まずはDashboardのデプロイから。
Dashboardの設定
- kube-controller-managerの設定で、ServiceAccountがAPIサーバとの通信に使うルートCA証明書を持つように設定する
# sed -i 's@KUBE_CONTROLLER_MANAGER_ARGS="@KUBE_CONTROLLER_MANAGER_ARGS="--root-ca-file=/etc/kubernetes/certs/apiserver.crt @g' /etc/kubernetes/controller-manager # systemctl restart kube-controller-manager
- 既存のServiceAccount「default」を削除(自動的に新しいCA証明書を持つServiceAccountが再作成されます)
# kubectl delete sa default # kubectl delete sa default -n kube-system # kubectl delete sa default -n kube-public
- APIサーバ側でsystem:anonymousがNamespace"kube-system"のResource"*"にRW権限を付ける
- ABACの設定
# vi /etc/kubernetes/abac-policy.json
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"system:anonymous", "namespace": "kube-system", "resource": "*"}}
- APIサーバの認可機能で、RBACも適用するように設定する。
# sed -i 's/--authorization-mode=ABAC/--authorization-mode=RBAC,ABAC/g' /etc/kubernetes/apiserver # systemctl restart kube-apiserver
- manifestを落としてくる
# curl -O https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml
- manifestの修正
- Dashboard自体のベーシック認証を有効化する
- オプション指定
- Dashboard自体のベーシック認証を有効化する
... spec: containers: - name: kubernetes-dashboard image: k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3 ports: - containerPort: 8443 protocol: TCP args: - --auto-generate-certificates - --authentication-mode # ここを追加 - basic # ここを追加 # Uncomment the following line to manually specify Kubernetes API server Host # If not specified, Dashboard will attempt to auto discover the API server and connect # to it. Uncomment only if the default does not work. # - --apiserver-host=http://my-address:port ...
- kubernetes-dashboardポッドのデプロイ
# kubectl create -f kubernetes-dashboard.yaml
次にDNSサービスのデプロイですねー
kube-dns設定
# sed -i 's/KUBELET_ARGS="/KUBELET_ARGS="--cluster-dns=172.16.0.2 --cluster-domain=cluster.local /g' /etc/kubernetes/kubelet # systemctl restart kubelet
- manifestをダウンロード
# curl -O https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/kube-dns.yaml.sed # mv kube-dns.yaml.sed kube-dns.yaml
# sed -i "s/\$DNS_SERVER_IP/172.16.0.2/g" kube-dns.yaml # sed -i "s/\$DNS_DOMAIN/cluster.local/g" kube-dns.yaml
- kube-dnsポッドのデプロイ
# kubectl create -f kube-dns.yaml
これでDashboardとDNSサービス用のPodなりServiceなりが正常に上がるはずです。
Dashboardはこんな感じ。
DNSサービスの稼働確認をするために、httpdポッドを二つ、別のノードに上げてみましょう。Masterノードで以下を叩きます。
# kubectl create deploy --image httpd httpd-1 # kubectl create deploy --image httpd httpd-2 # kubectl expose deploy httpd-1 --port 80 # kubectl expose deploy httpd-2 --port 80
ポッドが上がったら、以下のコマンドでcurlをそれぞれのポッドにインストールします。
# kubectl exec <httpd-1のポッド> -- apt-get update # kubectl exec <httpd-1のポッド> -- apt-get install -y curl # kubectl exec <httpd-2のポッド> -- apt-get update # kubectl exec <httpd-2のポッド> -- apt-get install -y curl
curlをインストールしたら、互いのサービスに対して名前解決ができるかを確かめます。
# kubectl exec <httpd-1のポッド> -- curl httpd-2 # kubectl exec <httpd-2のポッド> -- curl httpd-1