2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

kubeadm / Ansible で Kubernetes HA クラスタ構築ハンズオン

はじめに

前回 kubeadm で Kubernetes のシングルコントロールプレーンのクラスタを構築しました。

kubeadm / containerd で Kubernetes

今回は kubeadmAnsible を使って HA クラスタを構築しようと思います。
kubeadm を実行しようにも複数サーバをセットアップには時間がかかるので、Ansible を使って IaC な構築をしようと思います。
がっつりハンズオンな記事なので量がボリューミーなのはご了承ください。

環境情報

OS:Ubuntu 18.04.5 LTS
containerd:1.4.3
kubeadm:v1.20.2
Kubernetes:v1.20.2
kubectl:v1.20.2
Ansible:2.9.17

Role hostname CPU Memory enp0s3(NAT) enp0s8(Bridge)
ReverseProxy rp01 1 Core 1 Gi 10.0.2.15 192.168.10.51
ControlPlane cp01
cp02
cp03
2 Core 4 Gi 10.0.2.15 192.168.10.61
192.168.10.62
192.168.10.63
Node nd01
nd02
2 Core 4 Gi 10.0.2.15 192.168.10.71
192.168.10.72

あまりに気にしていなかったのですが、MasterControlPlaneWorkerNode と呼ばれているんですね。知らなかった・・・

Kubernetesのコンポーネント

今回も Vagrant/VirtualBox で構築します。
参考までに下記に今回使った Vagrantfile を載せておきます。
※NIC1(enp0s3):NAT、NIC2(enp0s8):ブリッジアダプターで設定しています。
kubectl exec で支障がでるため、起動スクリプトでデフォルトゲートウェイをブリッジアダプター側(192.168.10.1)に変更しています。

Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.define "rp01" do |server|
    server.vm.box = "ubuntu/bionic64"
    server.vm.hostname = "rp01"
    server.vm.network "public_network", ip: "192.168.10.51"    
    server.vm.synced_folder ".", "/vagrant", disabled: true
    server.vm.provider "virtualbox" do |vb|
      vb.customize [
        "modifyvm", :id,
        "--memory", "1024",
        "--cpus", "1"
      ]
    end
    server.vm.provision "shell", run: "always", inline: <<-SHELL
sudo ip route del default via 10.0.2.2
sudo ip route add default via 192.168.10.1
    SHELL
  end

  (1..3).each do |i|
    config.vm.define "cp0#{i}" do |server|
      server.vm.box = "ubuntu/bionic64"
      server.vm.hostname = "cp0#{i}"
      server.vm.network "public_network", ip: "192.168.10.6#{i}"    
      server.vm.synced_folder ".", "/vagrant", disabled: true
      server.vm.provider "virtualbox" do |vb|
        vb.customize [
          "modifyvm", :id,
          "--memory", "4096",
          "--cpus", "2"
        ]
      end
      server.vm.provision "shell", run: "always", inline: <<-SHELL
sudo ip route del default via 10.0.2.2
sudo ip route add default via 192.168.10.1
      SHELL
    end
  end

  (1..2).each do |i|
    config.vm.define "nd0#{i}" do |server|
      server.vm.box = "ubuntu/bionic64"
      server.vm.hostname = "nd0#{i}"
      server.vm.network "public_network", ip: "192.168.10.7#{i}"    
      server.vm.synced_folder ".", "/vagrant", disabled: true
      server.vm.provider "virtualbox" do |vb|
        vb.customize [
          "modifyvm", :id,
          "--memory", "4096",
          "--cpus", "2"
        ]
      end
      server.vm.provision "shell", run: "always", inline: <<-SHELL
sudo ip route del default via 10.0.2.2
sudo ip route add default via 192.168.10.1
      SHELL
    end
  end
end

Ansible インストール

まずはサーバセットアップ用に Ansible をインストールしていきます。

作業サーバ:rp01
# Ansible インストールのため python のパッケージマネージャ pip をインストール
$ sudo apt update && sudo apt install python3-pip -y

$ pip3 -V
pip 9.0.1 from /usr/lib/python3/dist-packages (python 3.6)

# Ansible のインストール(今回はバージョンを指定してインストール)
$ sudo pip3 install ansible==2.9.17

$ ansible --version
ansible 2.9.17
・・・

インストールができたので Ansible で各サーバに SSH 接続するための鍵ファイルを作って配布します。
今回は vagrant で実行しているため、vagrant ユーザに公開鍵を追加しています。適宜、作業用のユーザに設定ください。

作業サーバ:rp01
# Ansible 用の SSH 鍵作成
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/vagrant/.ssh/id_rsa): ★Enter
Enter passphrase (empty for no passphrase): ★Enter
Enter same passphrase again: ★Enter
・・・

# rp01 の vagrant ユーザの公開鍵に鍵を追加
$ cat .ssh/id_rsa.pub >> .ssh/authorized_keys

# 他サーバの vagrant ユーザに公開鍵を追加するため、対象公開鍵を確認
$ cat .ssh/id_rsa.pub
ssh-rsa xxxx root@rp01

# 上記で確認した公開鍵を、各サーバ(cp01~03、nd01~02)にログインし vagrant ユーザの公開鍵に作成した鍵を追記
# vagrant の場合、デフォルトの鍵は消すと SSH ログインできなくなるため、消さずに追記する
$ vi ~/.ssh/authorized_keys

各サーバへの接続準備ができました。Ansible で接続する際の設定ファイルを作成して疎通確認を行っていきます。

作業サーバ:rp01
# 作業用ディレクトリ作成
$ mkdir -p kubeadm/roles && cd kubeadm

# 以降で以下の構成でファイルを作成する
kubeadm/
 ├ roles/
 ├ ansible.cfg  :Ansible 設定ファイル
 └ inventory    :接続先情報ファイル

初回 SSH 接続時の know_hosts 警告文の対応に Ansible の設定ファイルを作成します。
セキュアな接続を行う場合は、一度各サーバへ SSH 接続確認を行うことで以下の設定は不要です。

参考:AnsibleのSSH接続エラーの回避設定

ansible.cfg
[defaults]
host_key_checking = False

接続先サーバの情報ファイルを作成します。
各サーバの IP アドレスとグループを分けて登録しました。

inventory
[rp]
rp01  ansible_host=192.168.10.51

[cp]
cp01  ansible_host=192.168.10.61
cp02  ansible_host=192.168.10.62
cp03  ansible_host=192.168.10.63

[nd]
nd01  ansible_host=192.168.10.71
nd02  ansible_host=192.168.10.72

準備ができたの Ansible で疎通確認を行います。

作業サーバ:rp01
# ad-hoc 機能で Ansible のモジュールを直接実行し、接続確認を行う
# -i:接続先情報ファイルを指定
# -m:Ansible モジュール指定。ping モジュールは疎通確認を行う
# all:接続先の引数、inventory ファイルで記載した識別子が指定可能。今回は全てのサーバを指定する all を指定
$ ansible -i inventory -m ping all
cp03 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
・・・
nd02 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

ping - pong で無事に疎通確認が取れました。

共通設定

さて、ここから Ansible の Playbook を作成していきます。
今回は Roles を使って Playbook を取り纏めます。
Roles の簡単な説明は、以下記事を投稿しているのでご参考に。

ansible role 事始め

手始めに共通で利用する Role を作成します。

作業サーバ:rp01
# 以下の構成でファイルを作成する
kubeadm/
 ├ ansible.cfg
 ├ inventory
 └ roles/                   :各ロール用のディレクトリを配下に配置
    └ common/               :共通設定のロール
       └ tasks/main.yml     :共通の設定タスク

$ pwd
/root/kubeadm

# 共通設定のロールを作成
# 以下コマンドでロールのテンプレート一式が作成される
# --init-path:ロールのテンプレートを指定パス配下に作成する
$ ansible-galaxy role init --init-path roles common
- Role common was created successfully

$ ls roles/
common

最初の Role ができた所で Playbook を作成します。
共通で利用するパッケージのインストールを行うタスクを記述していきます。

Ansible:apt

roles/common/tasks/main.yml
---
# tasks file for common
- name: Install https repo for apt
  apt:
    pkg:
    - apt-transport-https
    - ca-certificates
    - curl
    - software-properties-common
    update_cache: yes
    state: present

実際のロールの指定、実行は以降で合わせて実施します。

ReverseProxy 構築

次に rp01 にリバースプロキシとして nginx をインストールしていきます。
kubectl を使ったコントロールプレーンへのリクエストを振り分けます。

作業サーバ:rp01
# 以下の構成でファイルを作成する
kubeadm/
 ├ ansible.cfg
 ├ inventory
 ├ rp.yml                   :リバースプロキシ実行用 Playbook
 └ roles/
    └ reverseproxy/         :リバースプロキシのロール
       ├ files/nginx.conf   :nginx 設定ファイル
       ├ handlers/main.yml  :nginx の設定ファルが書き換わったときの reload Playbook
       └ tasks/main.yml     :nginx インストール・設定 Playbook

# リバースプロキシのロール作成
$ ansible-galaxy role init --init-path roles reverseproxy
- Role reverseproxy was created successfully

$ ls roles/
common reverseproxy

ロールが作成できたところで、nginx インストールの Playbook を作成していきます。
Playbook のタスク内容は以下の通りです。

  1. nginx のインストール
  2. 設定ファイルの配置
  3. nginx の起動と自動起動の設定

Ansible:aptcopysystemd

roles/reverseproxy/tasks/main.yml
---
# tasks file for reverseproxy
- name: Install nginx
  apt:
    name: nginx
    update_cache: yes
    state: present

- name: Copy nginx conf
  copy:
    src: nginx.conf
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
    backup: yes
  notify: reload_nginx

- name: Start/Enabled nginx
  systemd:
    name: nginx
    enabled: yes
    state: started

nginx.conf が変更された際にも自動で reload されるように handlers に処理を記載します。

roles/reverseproxy/handlers/main.yml
---
# handlers file for reverseproxy
- name: reload_nginx
  systemd:
    name: nginx
    state: reloaded

task.yaml で指定した notify: reload_nginx はファイルの再配置をトリガーに handlers に通知し、 reload_nginx を実行する仕組みとなっています。

次に Playbook で配置する nginx の設定ファイルを作成します。今回は最低限の設定のみ記述しています。
※ k8s api リクエストを各コントロールプレーンにリバースプロキシします。

roles/reverseproxy/files/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

error_log /var/log/nginx/error.log warn;

events {
   worker_connections  1024;
}

stream {
   upstream kubernetes {
      server 192.168.10.61:6443 max_fails=2 fail_timeout=30s;
      server 192.168.10.62:6443 max_fails=2 fail_timeout=30s;
      server 192.168.10.63:6443 max_fails=2 fail_timeout=30s;
   }
   server {
      listen 6443;
      proxy_pass kubernetes;
   }
}

最後に作成したロールを実行するための Playbook を作成します。rp グループに所属するサーバに対し、reverseproxy ロールと先ほど作成した common ロールの Playbook を実行するように作成します。

  • hosts:実行対象
  • gather_facts:対象サーバのメタ情報取得要否。OS の種別によって Playbook を変更する場合などの条件分岐させる際に便利。
  • become:指定されたユーザでの実行要否。yes を指定すると、デフォルトは root ユーザになって実行する。
  • tags:Playbook 実行時に指定すると実行対象を絞り込む事ができる
rp.yml
---
# file: rp.yml
- hosts: rp
  gather_facts: no
  become: yes
  roles:
    - role: common
      tags: cmn
    - role: reverseproxy
      tags: rp

準備が整ったので実行していきます。
1度目の実行で設定を適用し、2度目の実行で設定がちゃんと反映されているか確認します。
-C オプションで dry-run、--diff オプションで変更前後の diff を出力することもできます。
※ 対象ユーザにパスワードなしの sudo の許可設定がされていない場合はエラーが出ます。vagrant の場合は /etc/sudoers.d/vagrantvagrant ALL=(ALL) NOPASSWD:ALL が設定されています。必要に応じて設定をお願いします。

作業サーバ:rp01
$ ansible-playbook -i inventory rp.yml

PLAY [rp] ***************************************************************************************
・・・
TASK [reverseproxy : Install nginx] *************************************************************
changed: [rp01]
・・・
PLAY RECAP **************************************************************************************
rp01     : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ ansible-playbook -i inventory rp.yml
・・・
PLAY RECAP **************************************************************************************
rp01     : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

全て ok になり、無事に nginx のインストール、設定ファイルの配置が完了しました。
プロキシ先が無いため、とりあえずポートの疎通確認だけ確認します。

作業サーバ:rp01
$ nc 192.168.10.51 -z 6443 -v
Connection to 192.168.10.51 6443 port [tcp/*] succeeded!

Ansible で Kubernetes セットアップ

最初の Playbook が実行できたので今度は Kubernetes サーバに適用する Playbook を作成していきます。
まずは全てのロールを作成します。

作業サーバ:rp01
# ロールのディレクトリ構成
kubeadm/
 └ roles/
    ├ cri/          :CRI(containerd)のロール
    ├ k8s-server/   :Kubernetes サーバ(kubelet / kubeadm )のロール
    └ k8s-client/   :Kubernetes クライアント(kubectl)のロール

# 各ロールの作成
$ ansible-galaxy role init --init-path roles cri
- Role cri was created successfully

$ ansible-galaxy role init --init-path roles k8s-server
- Role k8s-server was created successfully

$ ansible-galaxy role init --init-path roles k8s-client
- Role k8s-client was created successfully

# ls roles/
common  cri  k8s-client  k8s-server  reverseproxy

cri

まずはコンテナ実行環境(CRI)として containerd をインストールします。

作業サーバ:rp01
# cri ロールのディレクトリ構成
kubeadm/
 ├ cp.yml                       :コントロールプレーン実行用 Playbook
 ├ nd.yml                       :ノード実行用 Playbook
 └ roles/
    └ cri/                      :CRI(containerd)のロール
        ├ files/containerd.conf  :カーネルモジュール永続化用ファイル
       └ tasks/main.yml         :Playbook

前述より長めの Playbook ですが、やっていることは以下のとおりです。

  1. カーネルモジュールのロード
  2. カーネルモジュール設定の永続化用にファイル配置
  3. カーネルパラメータの設定
  4. リポジトリの登録
  5. containerd のインストール

Ansible:modeprobecopysysctlapt_keyapt_repositoryaptsystemd

roles/cri/tasks/main.yml
---
# tasks file for cri
- name: Load kernel module
  modprobe:
    name: "{{ item }}"
    state: present
  with_items:
    - br_netfilter
    - overlay

- name: Copy kernel module load list for boot
  copy:
    src: containerd.conf
    dest: /etc/modules-load.d/containerd.conf
    owner: root
    group: root
    mode: '0644'

- name: Add sysctl conf
  sysctl:
    name: "{{ item.name }}"
    value: "{{ item.value }}"
    sysctl_file: /etc/sysctl.d/99-kubernetes-cri.conf
    state: present
  with_items:
    - name: net.ipv4.ip_forward
      value: "1"
    - name: net.bridge.bridge-nf-call-iptables
      value: "1"
    - name: net.bridge.bridge-nf-call-ip6tables
      value: "1"

- name: Add apt-key for docker
  apt_key:
    url: https://download.docker.com/linux/ubuntu/gpg
    state: present

- name: Add apt repository for docker
  apt_repository:
    repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable
    state: present

- name: Install containerd
  apt:
    name: containerd.io
    update_cache: yes
    state: present

- name: Start/Enabled containerd
  systemd:
    name: containerd
    enabled: yes
    state: started

カーネルモジュールの設定の永続化は設定ファイルが必要なため、以下ファイルを作成して配置します。

roles/cri/files/containerd.conf
overlay
br_netfilter

CRI インストールの Playbook ができたので、コントロールプレーン、ノードの実行用 Playbook を作成します。

cp.yml
---
# file: cp.yml
- hosts: cp
  gather_facts: no
  become: yes
  roles:
    - role: common
      tags: cmn
    - role: cri
      tags: cri
nd.yml
---
# file: nd.yml
- hosts: nd
  gather_facts: no
  become: yes
  roles:
    - role: common
      tags: cmn
    - role: cri
      tags: cri

実行用 Playbook ができたのでいざ実行です。

作業サーバ:rp01
$ ansible-playbook -i inventory cp.yml
PLAY [cp] ***************************************************************************************
・・・
PLAY RECAP **************************************************************************************
cp01    : ok=8    changed=7    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
cp02    : ok=8    changed=7    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
cp03    : ok=8    changed=7    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ ansible-playbook -i inventory nd.yml
PLAY [nd] ***************************************************************************************
・・・
PLAY RECAP **************************************************************************************
nd01    : ok=8    changed=7    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
nd02    : ok=8    changed=7    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

CRI の containerd がインストールできました。

k8s-server

CRI がインストールできたので、今度はサーバ側で必要な Kubernetes のパッケージをインストールしていきます。
Kubernetes コンポーネントである kubelet と、インストールツールである kubeadm をインストールする Playbook を作成していきます。

作業サーバ:rp01
_/_/_/ k8s-server ロールのディレクトリ構成 _/_/_/ 
kubeadm/
 └ roles/
    └ k8s-server/               :Kubernetes サーバ(kubelet / kubeadm )のロール
       └ tasks/main.yml         :Playbook

インストールの流れは今までと同じような以下の流れです。

  1. Kubernetes レポジトリ登録
  2. kubeletkubeadm のインストール
roles/k8s-server/tasks/main.yml
---
# tasks file for k8s-server
- name: Add apt-key for kubernetes
  apt_key:
    url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
    state: present

- name: Add apt repository for kubernetes
  apt_repository:
    repo: deb https://apt.kubernetes.io/ kubernetes-xenial main
    state: present

- name: Install kubeadm, kubelet
  apt:
    pkg:
    - kubelet
    - kubeadm
    update_cache: yes
    state: present

先程作成した、cp.ymlnd.yml にロールを追加して実行します。

cp.yml
---
# file: cp.yml
- hosts: cp
  gather_facts: no
  become: yes
  roles:
    - role: common
      tags: cmn
    - role: cri
      tags: cri
    - role: k8s-server
      tags: k8ss
nd.yml
---
# file: nd.yml
- hosts: nd
  gather_facts: no
  become: yes
  roles:
    - role: common
      tags: cmn
    - role: cri
      tags: cri
    - role: k8s-server
      tags: k8ss

Playbook にタグを付与したので、タグを指定して追加した Playbook のみ実行します。

作業サーバ:rp01
$ ansible-playbook -i inventory -t k8ss cp.yml
PLAY [cp] ***************************************************************************************
・・・
PLAY RECAP **************************************************************************************
cp01    : ok=3    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
cp02    : ok=3    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
cp03    : ok=3    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ ansible-playbook -i inventory -t k8ss nd.yml
PLAY [nd] ***************************************************************************************
・・・
PLAY RECAP **************************************************************************************
nd01    : ok=3    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
nd02    : ok=3    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

kubeadm の実行前の準備が整いました。

k8s-client

kubeadm を実行する前に もう一つ Kubenetes を操作するクライアントツール(kubectl)を rp01 にインストールしておきます。

_/_/_/ k8s-client ロールのディレクトリ構成 _/_/_/ 
kubeadm/
 └ roles/
    └ k8s-client/               :Kubernetes クライアント(kubectl)のロール
       └ tasks/main.yml         :Playbook

Playbook では kubectl のインストールと合わせて、エイリアス(kc)と補完用の設定を追記します。
blockinfile モジュールの中で .bashrc に設定を追記しています。適宜 path は変更ください。

Ansible:blockinfile

roles/k8s-client/tasks/main.yml
---
# tasks file for k8s-client
- name: Add apt-key for kubernetes
  apt_key:
    url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
    state: present

- name: Add apt repository for kubernetes
  apt_repository:
    repo: deb https://apt.kubernetes.io/ kubernetes-xenial main
    state: present

- name: Install kubectl
  apt:
    pkg:
    - kubectl
    update_cache: yes
    state: present

- name: Add kubectl alias(kc) and register completion
  blockinfile:
    path: /home/vagrant/.bashrc
    block: |
      source <(kubectl completion bash)
      alias kc=kubectl
      complete -F __start_kubectl kc
    insertafter: EOF

rp.yml に追加した Role を追記して実行します。

rp.yml
---
# file: rp.yml
- hosts: rp
  gather_facts: no
  become: yes
  roles:
    - role: common
      tags: cmn
    - role: reverseproxy
      tags: rp
    - role: k8s-client
      tags: k8sc

タグを指定して実行します。

作業サーバ:rp01
$ ansible-playbook -i inventory -t k8sc rp.yml
PLAY [rp] ***************************************************************************************
・・・
PLAY RECAP **************************************************************************************
rp01     : ok=4    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Playbooks

今まで作成した資材を以下のとおりです。

kubeadm/
 ├ ansible.cfg                  :Ansible 設定ファイル
 ├ inventory                    :接続先情報ファイル
 ├ rp.yml                       :リバースプロキシ実行用 Playbook
 ├ cp.yml                       :コントロールプレーン実行用 Playbook
 ├ nd.yml                       :ノード実行用 Playbook
 └ roles/
    ├ common/                   :共通設定のロール
    │  └ tasks/main.yml
    ├ reverseproxy/             :リバースプロキシのロール
    │ ├ files/nginx.conf
    │ ├ handlers/main.yml
    │ └ tasks/main.yml
    ├ cri/                      :CRI(containerd)のロール
     │ ├ files/containerd.conf
    │ └ tasks/main.yml
    ├ k8s-server/               :Kubernetes サーバ(kubelet / kubeadm )のロール
    │ └ tasks/main.yml
    └ k8s-client/               :Kubernetes クライアント(kubectl)のロール
       └ tasks/main.yml

kubeadm でクラスタ構築

kubeadm の実行準備ができたのでコントロールプレーンから構築していきます。

先んじてエラー対応のために、containerd の初期設定ファイルの削除とデーモンの再起動を行います。

https://github.com/containerd/containerd/issues/4581

作業サーバ:rp01
# file モジュールを使って設定ファイル削除
$ ansible -i inventory cp,nd -m file -a "path=/etc/containerd/config.toml state=absent" --become

nd02 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": true,
    "path": "/etc/containerd/config.toml",
    "state": "absent"
}
・・・

# systemd モジュールを使ってデーモン再起動
$ ansible -i inventory cp,nd -m systemd -a "name=containerd state=restarted" --become

cp03 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": true,
    "name": "containerd",
    "state": "started",
    "status": {
・・・

最初の一台目のコントロールプレーンを構築します。
cp01 にログインし、kubeadm を実行します。

作業サーバ:cp01
_/_/_/ 一台目のコントロールプレーンを構築 _/_/_/
# --control-plane-endpoint:コントロールプレーンのエンドポイント
# ⇒ 今回はリバースプロキシのサーバを経由してコントロールプレーンへのリクエストを受けるため指定
# --pod-network-cidr:Pod に割り振られる IP アドレスレンジ
# ⇒ デフォルトだと LAN と被ったので指定
# --apiserver-advertise-address:API サーバーが待ち受けする IP アドレス
# ⇒ Vagrant デフォルトだと NAT の NIC で動作不良起こしそうだったので、ブリッジの NIC を指定
# --upload-certs:コントロールプレーンノードで共有する必要がある証明書をクラスタにアップロード(Secret)
# ⇒ 複数コントロールプレーンノードを作成する場合は指定(手動で証明書配布する場合は不要)
# ⇒ インストール出力を見るに有効期限は2時間?
# 参考:https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/high-availability/#manual-certs

$ sudo kubeadm init \
--control-plane-endpoint "192.168.10.51:6443" \
--pod-network-cidr 172.16.0.0/16 \
--apiserver-advertise-address 192.168.10.61 \
--upload-certs

[init] Using Kubernetes version: v1.20.2
・・・
Your Kubernetes control-plane has initialized successfully!

・・・

You can now join any number of the control-plane node running the following command on each as root:

  kubeadm join 192.168.10.51:6443 --token xxxx \
    --discovery-token-ca-cert-hash sha256:xxxx \
    --control-plane --certificate-key xxxx

・・・

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.10.51:6443 --token xxxx \
    --discovery-token-ca-cert-hash sha256:xxxx

1~3分ほどで無事にインストールができると思います。
出力内容のトークンなどはこの後利用するので、結果は保存しておきましょう。

続いてネットワークプラグイン(Project Calico)をインストールします。
インストールには kubectl を使うため、接続用の設定ファイルを cp01 から取得し rp01 に配置します。

作業サーバ:rp01
# Kubernetes に接続する際の設定ファイルを取得
$ mkdir ~/.kube
$ ssh 192.168.10.61 "sudo cat /etc/kubernetes/admin.conf" > ~/.kube/config

$ kubectl get node
NAME   STATUS     ROLES                  AGE   VERSION
cp01   NotReady   control-plane,master   30m   v1.20.2

# ネットワークプラグインのデプロイ
$ kubectl apply -f https://docs.projectcalico.org/v3.17/manifests/calico.yaml
・・・
daemonset.apps/calico-node created
・・・

$ kubectl get node
NAME   STATUS   ROLES                  AGE   VERSION
cp01   Ready    control-plane,master   32m   v1.20.2

1台目のコントロールプレーンが無事に起動できたところで、2台目 / 3台目を追加します。
実行用のコマンドは1台目をインストールした際に出力されています。今回はそちらに --apiserver-advertise-address オプションで IP アドレスを指定しています(以下コマンドは改行をいれて整形しています)。
cp02cp03 にログインし実行していきます。

作業サーバ:cp02/cp03
# cp02
sudo kubeadm join 192.168.10.51:6443 \
--token xxxx \
--discovery-token-ca-cert-hash sha256:xxxx \
--certificate-key xxxx \
--control-plane \
--apiserver-advertise-address 192.168.10.62

# cp03
sudo kubeadm join 192.168.10.51:6443 \
--token xxxx \
--discovery-token-ca-cert-hash sha256:xxxx \
--certificate-key xxxx \
--control-plane \
--apiserver-advertise-address 192.168.10.63

コントロールプレーンが HA 構成の3台で稼働できたことを確認します。

作業サーバ:rp01
$ kubectl get node
NAME   STATUS   ROLES                  AGE     VERSION
cp01   Ready    control-plane,master   41m     v1.20.2
cp02   Ready    control-plane,master   2m49s   v1.20.2
cp03   Ready    control-plane,master   77s     v1.20.2

次にノードを追加します。こちらもアドレスを指定して実行します。実行コマンドはコントロールプレーンの1台目をインストールした時に出力されています。コントロールプレーンの2台目、3台目をインストールした時と同様に IP アドレスを指定して実行します。

作業サーバ:nd01/nd02
# nd01
$ sudo kubeadm join 192.168.10.51:6443 \
--token xxxx \
--discovery-token-ca-cert-hash sha256:xxxx \
--apiserver-advertise-address 192.168.10.71

# nd02
$ sudo kubeadm join 192.168.10.51:6443 \
--token xxxx \
--discovery-token-ca-cert-hash sha256:xxxx \
--apiserver-advertise-address 192.168.10.72

ノードが2台構成で稼働していることを確認します。

作業サーバ:rp01
$ kc get node -o wide
NAME   STATUS   ROLES                  AGE     VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIME
cp01   Ready    control-plane,master   9m30s   v1.20.2   192.168.10.61   <none>        Ubuntu 18.04.5 LTS   4.15.0-124-generic   containerd://1.4.3
cp02   Ready    control-plane,master   5m3s    v1.20.2   192.168.10.62   <none>        Ubuntu 18.04.5 LTS   4.15.0-124-generic   containerd://1.4.3
cp03   Ready    control-plane,master   3m51s   v1.20.2   192.168.10.63   <none>        Ubuntu 18.04.5 LTS   4.15.0-124-generic   containerd://1.4.3
nd01   Ready    <none>                 2m45s   v1.20.2   192.168.10.71   <none>        Ubuntu 18.04.5 LTS   4.15.0-124-generic   containerd://1.4.3
nd02   Ready    <none>                 99s     v1.20.2   192.168.10.72   <none>        Ubuntu 18.04.5 LTS   4.15.0-124-generic   containerd://1.4.3

動作確認

クラスタが構築できたので動作確認をします。

# Pod 作成
$ kubectl run nginx --image=nginx --port=80
pod/nginx created

$ kubectl get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP             NODE   NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          22s   172.16.246.1   nd01   <none>           <none>

# Pod 内でバージョン確認
$ kubectl exec nginx -- nginx -v
nginx version: nginx/1.19.6

# Service(NodePort)を公開
$ kubectl expose pod nginx --type=NodePort
service/nginx exposed

# Worker の IP、Service の NodePort を取得して Pod にアクセス
$ IP=`kubectl get node nd01 -o=jsonpath='{.status.addresses[?(@.type == "InternalIP")].address}'`
$ PORT=`kubectl get svc nginx -o yaml -o=jsonpath='{.spec.ports[0].nodePort}'`

$ curl -I http://$IP:$PORT/
HTTP/1.1 200 OK
Server: nginx/1.19.6
Date: Fri, 12 Feb 2021 17:18:00 GMT
Content-Type: text/html
・・・

まとめ

長いハンズオンお疲れさまでした。Ansible でコード化することで、複数サーバで実行しなければならないコマンドも一発で実行することができたかと思います。また、1度作成したコード群は追加サーバのセットアップなど、その後のクラスタ構築の時間を短縮することができます。ひいてはいつでも壊せるクラスタを構築することができるのではと思います。
今回はコントロールプレーンやノードに特別な設定を加えていませんが、OS の設定(ユーザ追加やディスク管理)や個々のサーバへの設定を追加していくこともできるので応用の幅は広いかと思います。
kubeadm って本当に便利ですね、開発者に感謝です。

参考

kubeadmを使ってクラスターを構築する
kubeadmを使用した高可用性クラスターの作成

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
2
Help us understand the problem. What are the problem?