なからなLife

geekに憧れと敬意を抱きながら、SE、ITコンサル、商品企画、事業企画、管理会計、総務・情シス、再び受託でDB屋さんと流浪する人のブログです。

「15 Stepで習得 Dockerから入るKubernates」を読んでEC2で演ってみた:(1)概要と環境構築

やっとkubernetesに手を出した

本職がDB専業、MySQLPostgreSQLOracleの導入、チューニング、移行などの支援がメインということもあり、k8sを使うインセンティブもモチベーションも非常に低かったのです。


しかし、流石にぼちぼち触っておこうと思い、このコロナ禍での在宅勤務と、稼働率が少し落ち着いてきたのが重なって自由な時間が増えたところに、この本と出会ったという色々な偶然が重なったので一念発起しました。

k8sは進化が早くて、書籍はあっという間に陳腐化するし、ネット上に散財している情報はチュートリアル的に一気通貫で学べるものが少ないなど、非常にとっかかりにくい状況でしたが、この本がまさに求めていたタイプの本でした。

なお、2年くらい前にDockerについて勉強して、一通りの概念、操作は理解したものの、マルチノード環境の構築のところでFlannelが正しく機能するように構築しきれなかったところで挫折した程度のスキル、からのスタートです。


書いていたら、かなり長くなってしまったので、複数回に分けます。
(1)概要と環境構築
(2)各演習(Step)でのメモ



まず、全体の感想。

解説が非常に丁寧です。学習のためにKubenetesが一通り動くようになる環境の構築もVagrantベースで用意されていて「Kubernetesの環境が構築された上」で一連の操作を習得するのに、非常によいテキストといえます。当然、他の人に勧めたい本です。

Minikubeだけではなく、マルチノードなk8sクラスターもVagrant(+Ansible)で立ち上がり、さらに、k8sの外に用意する永続化層としてのNFSサーバーやGlusterFS(+Heketi)クラスタ駆逐のためのVagrantFileもGithub上に用意されいて、とても親切。
そして、クラウドについても、IKS(IBM Cloud Kubernetes Service)とGKE(Google Kubernetes Engine)を取り上げています。


というか、学習環境については、minikubeベースやGCPベースの解説の記事、書籍は結構多いので、ここはマルチノードクラスタのところに特化して、自動構築の環境提供よりも自力でできる構築手順=Kubernetesや組み合わせて使われがちなプロダクトが動く環境を作る方法の解説に厚みをもたせたほうがよかったんじゃないか。というのは、私個人のニーズとしては、環境構築「も」しっかり学びたかったので、学習環境の自動構築よりも構築手順の解説がほしかったわけですね。(超わがまま


そんなわけで、あえてこのVagrantベースの環境なしで、あえて「AWS上にEC2で、EKS使わずに構築」して、この本の演習をやってみました。

残念ながらAWS、Azure、OracleCloudでの解説はないので、ググったり、著者提供のVagrantFileやAnsibleのymlを読んだりして、どうにかこうにか進めました。

Rancherとか簡単に構築できるツールや、EKS等のマネージド・サービスの誘惑もありましたが、一度苦しんで、マネージド等のありがたみを後から知るというマゾ志向で。

(15ステップ終わる直前まで、
github.com
の存在を知らず。ほんとに深く苦しみ知りたかったらコレだ、と思った。)


EKSについては、「Kubernetes on AWS~アプリケーションエンジニア 本番環境へ備える」が最近出てきたので、追々こちらの本でしっかりキャッチアップしていこうかな、と思ってます。


このエントリは、その学習記録であり、書籍通りにやろうとするとEC2で動かすとうまく行かない部分のメモ展開でもあります。
また、Minikubeベースの学習環境を前提とした解説ページも多いので、minikube使わないときにどうしたか、というところも触れていきます。

やったこと全部書くのではなく、躓いたところだけです。
もし同じことをやろうとしている人がいたら参考になるかも、ということで。


なお、購入したのはKindle版です。
もしかしたら誤植?な部分もあります。




AWS上に用意した学習環境

Kubernetesクラスタ環境
  • EC2
    • タイプと数:t3.small*3
    • 2vCPU、2GBメモリないと、構築時に使うkubeadmが動かない、とのことだったので。
  • EBS:gp2 8GB(デフォルト)
  • OS:AmazonLinux2


kubeadmでAmazon Linux2にKubernetes1.18 - Qiita
を参考にしつつ、DockerやKubenetesのインストール時にバージョン指定せず最新を取得。
記事の後半にあるRedisは入れません。

sudo  hostnamectl set-hostname k8s-master   # 各ノードに対応した名前を設定
sudo vi /etc/hosts
(master,worker1,worker2,heketi,glusterfs1,glusterfs2,glusterfs3のローカルIPを列挙)
sudo yum install -y docker
sudo systemctl start docker && sudo systemctl enable docker
sudo usermod -a -G docker $USER
exit  # ログアウトして、すぐに再ログイン
sudo tee /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ],
  "insecure-registries": [
    "k8s-master:5000"
  ]
}
EOF
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo tee /etc/sysctl.d/k8s.conf <<EOF
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward                 = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sudo sh -c "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=0
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
"
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
sudo systemctl enable kubelet && sudo systemctl start kubelet
sudo yum install -y tc


マスターノードのみ

sudo kubeadm init --pod-network-cidr 192.168.0.0/16 
sudo mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
sudo kubectl apply -f https://docs.projectcalico.org/v3.13/manifests/calico.yaml

kubeadm token create --print-join-command


この最後にkubeadm token createした出力結果「sudo kubeadm join」を、ワーカーノードそれぞれにコピペ実行。

sudo kubeadm join マスターノードのIPアドレス:6443 --token ・・


マスターノードに戻って

kubectl label node k8s-worker1 node-role.kubernetes.io/worker=
kubectl label node k8s-worker2 node-role.kubernetes.io/worker=


これで
master
worker1
worker2
の環境構築が一旦完了。


インストールバージョンを細かく指定しなかった結果

という組み合わせになっています。


プライベートリポジトリは、
Docker プライベートレジストリを構築する手順 - hirosanote’s blog
を参考に、masterノード上で

# docker pull registry
# docker run -dit -p 5000:5000 --name registry registry

でプレーンドッカーレジストリを起動しただけです。


なお、プライベートなプレーンドッカーレジストリを使う場合、「セキュリティの低い環境」ということで、
/etc/docker/daemon.json

{
  "insecure-registries": [
    "プレーンドッカーレジストリが動いているノードのホスト名:ポート番号"
  ]
}

を加える必要があります。(上記セットアップ「sudo tee /etc/docker/daemon.json <

sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo yum-config-manager --enable epel
sudo yum -y install glusterfs glusterfs-fuse

を実行します。

実際には、依存性解決で
glusterfs-client-xlators
glusterfs-libs
もインストールされます。




NFSとしての設定は以下の通り
EC2上にNFSサーバ/クライアント構築する - Qiita
を参考に構築します。

sudo mkdir /export
sudo vi /etc/exports
/export 10.10.0.0/255.255.0.0(rw,no_root_squash)

sudo systemctl status nfs
sudo systemctl start nfs
sudo systemctl enable nfs
GlusterFS環境
  • EC2
    • タイプと数:t2.micro*4
      • GlusterFSクラスター用*3(gluster1,gluster2,gluster3)
      • Heketiサーバー用*1
  • EBS:gp2 8GB(デフォルト)
    • GlusterFS用のインスタンスだけ、セカンダリとして20GBのEBS-gp2 20GBを/dev/sdfとしてアタッチ。フォーマットはしない。(GlusterFS+Heketiのときはフォーマット&マウントしなくていいらしい)
  • OS:Ubuntu 18.04.4 LTS

全部、AWS VPC上のパブリックな同一サブネット上に立ち上げ、セキュリティグループは、同一グループ内はフルアクセスな状態にしました。



なんで、OSバラバラな環境にしたかって?
たまたまEC2上にkubeadmで構築するのに参考にしたサイトが、AmazonLinux2だったからです。
そして、GlusterFSを使う演習に入るタイミングでGlusterFSのことを調べ始めたら、ubuntuベースの記事が一番わかりやすかったからです。

結局、それが原因の1つとなって、後でドハマリするのですが。


GlusterFSの環境構築は
Install - Gluster Docs
を参考に進めました。

始める前に、20GBのEBS-gp2をgluster1、gluster2、gluster3、に/dev/sdfとしてアタッチしておきます。

なお、EC2でAmazonLinuxばかり使っているとハマるのですが、ubuntuで立てたインスタンスのデフォルトユーザは「ec2-user」ではなく「ubuntu」です。

sudo hostnamectl set-hostname gluster1  # 各ノードに対応した名前を設定
sudo vi /etc/hosts
(master,worker1,worker2,heketi,glusterfs1,glusterfs2,glusterfs3のローカルIPを列挙)
sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:gluster/glusterfs-7
sudo apt-get install glusterfs-server
sudo apt-get update
sudo apt-get install thin-provisioning-tools glusterfs-client
sudo systemctl enable glusterd
sudo systemctl start glusterd

3台全部実施したあと、gluster1上で以下を実行する。

sudo gluster peer probe gluster2
sudo gluster peer probe gluster3


GlusterFSクラスターの構築が終わったあとで、Heketiサーバーを構築します。
GlusterFS + Heketi [Ub... | Wiki as a Service
の後半を参考にしました。

/etc/heketi/heketi.json
の「KEY_HERE」のところに、任意のパスワードを指定します。

wget https://github.com/heketi/heketi/releases/download/v9.0.0/heketi-v9.0.0.linux.amd64.tar.gz
tar -zxvf heketi-v9.0.0.linux.amd64.tar.gz
chmod +x heketi/{heketi,heketi-cli}
sudo cp heketi/{heketi,heketi-cli} /usr/local/bin
sudo groupadd --system heketi
sudo useradd -s /sbin/nologin --system -g heketi heketi
sudo mkdir -p /var/lib/heketi /etc/heketi /var/log/heketi
sudo vi /etc/heketi/heketi.json
(以下を記述)
{
  "_port_comment": "Heketi Server Port Number",
  "port": "8080",

	"_enable_tls_comment": "Enable TLS in Heketi Server",
	"enable_tls": false,

	"_cert_file_comment": "Path to a valid certificate file",
	"cert_file": "",

	"_key_file_comment": "Path to a valid private key file",
	"key_file": "",


  "_use_auth": "Enable JWT authorization. Please enable for deployment",
  "use_auth": false,

  "_jwt": "Private keys for access",
  "jwt": {
    "_admin": "Admin has access to all APIs",
    "admin": {
      "key": "KEY_HERE"
    },
    "_user": "User only has access to /volumes endpoint",
    "user": {
      "key": "KEY_HERE"
    }
  },

  "_backup_db_to_kube_secret": "Backup the heketi database to a Kubernetes secret when running in Kubernetes. Default is off.",
  "backup_db_to_kube_secret": false,

  "_profiling": "Enable go/pprof profiling on the /debug/pprof endpoints.",
  "profiling": false,

  "_glusterfs_comment": "GlusterFS Configuration",
  "glusterfs": {
    "_executor_comment": [
      "Execute plugin. Possible choices: mock, ssh",
      "mock: This setting is used for testing and development.",
      "      It will not send commands to any node.",
      "ssh:  This setting will notify Heketi to ssh to the nodes.",
      "      It will need the values in sshexec to be configured.",
      "kubernetes: Communicate with GlusterFS containers over",
      "            Kubernetes exec api."
    ],
    "executor": "ssh",

    "_sshexec_comment": "SSH username and private key file information",
    "sshexec": {
      "keyfile": "/etc/heketi/heketi_key",
      "user": "root",
      "port": "22",
      "fstab": "/etc/fstab"
    },

    "_db_comment": "Database file name",
    "db": "/var/lib/heketi/heketi.db",

     "_refresh_time_monitor_gluster_nodes": "Refresh time in seconds to monitor Gluster nodes",
    "refresh_time_monitor_gluster_nodes": 120,

    "_start_time_monitor_gluster_nodes": "Start time in seconds to monitor Gluster nodes when the heketi comes up",
    "start_time_monitor_gluster_nodes": 10,

    "_loglevel_comment": [
      "Set log level. Choices are:",
      "  none, critical, error, warning, info, debug",
      "Default is warning"
    ],
    "loglevel" : "debug",

    "_auto_create_block_hosting_volume": "Creates Block Hosting volumes automatically if not found or exsisting volume exhausted",
    "auto_create_block_hosting_volume": true,

    "_block_hosting_volume_size": "New block hosting volume will be created in size mentioned, This is considered only if auto-create is enabled.",
    "block_hosting_volume_size": 500,

    "_block_hosting_volume_options": "New block hosting volume will be created with the following set of options. Removing the group gluster-block option is NOT recommended. Additional options can be added next to it separated by a comma.",
    "block_hosting_volume_options": "group gluster-block",

    "_pre_request_volume_options": "Volume options that will be applied for all volumes created. Can be overridden by volume options in volume create request.",
    "pre_request_volume_options": "",

    "_post_request_volume_options": "Volume options that will be applied for all volumes created. To be used to override volume options in volume create request.",
    "post_request_volume_options": ""
  }
}
(ここまでを記述)

for i in dm_snapshot dm_mirror dm_thin_pool; do
  sudo modprobe $i
done
sudo ssh-keygen -f /etc/heketi/heketi_key -t rsa -N ''
sudo chown heketi:heketi /etc/heketi/heketi_key*

この後

for i in gluster1 gluster2 gluster3; do
  sudo ssh-copy-id -i /etc/heketi/heketi_key.pub root@$i
done

したいのですが、これがエラーになるので、
・/etc/heketi/heketi_key.pubをローカルPCにダウンロード
・GlusterFSの各ノードにアップロード(/home/ubuntu/に置いたことにする)
・GlusgerFSの各ノード上で以下を実行

sudo su -
cp /root/.ssh/authorized_keys /root/.ssh/authorized_keys.org
cat /home/ubuntu/heketi_key.pub >> /root/.ssh/authorized_keys

とします。



heketiサーバーに戻って

sudo vim /etc/systemd/system/heketi.service
(以下を記述)
[Unit]
Description=Heketi Server

[Service]
Type=simple
WorkingDirectory=/var/lib/heketi
EnvironmentFile=-/etc/heketi/heketi.env
User=heketi
ExecStart=/usr/local/bin/heketi --config=/etc/heketi/heketi.json
Restart=on-failure
StandardOutput=syslog
StandardError=syslog

[Install]
WantedBy=multi-user.target
(ここまでを記述)

sudo systemctl daemon-reload
sudo systemctl enable --now heketi
sudo chown -R heketi:heketi /var/lib/heketi /var/log/heketi /etc/heketi
sudo vim /etc/heketi/topology.json
(以下を記述。storage:の後に、各glusterノードのIPアドレス)
{
  "clusters": [
    {
      "nodes": [
                    {
          "node": {
            "hostnames": {
              "manage": [
                "gluster1"
              ],
              "storage": [
                "gluster1のIPアドレス"
              ]
            },
            "zone": 1
          },
          "devices": [
            "/dev/xvdf"
          ]
        },            {
          "node": {
            "hostnames": {
              "manage": [
                "gluster2"
              ],
              "storage": [
                "gluster2のIPアドレス"
              ]
            },
            "zone": 1
          },
          "devices": [
            "/dev/xvdf"
          ]
        },            {
          "node": {
            "hostnames": {
              "manage": [
                "gluster3"
              ],
              "storage": [
                "gluster3のIPアドレス"
              ]
            },
            "zone": 1
          },
          "devices": [
            "/dev/xvdf"
          ]
        }              
      ]
    }
  ]
}
(ここまでを記述)
sudo systemctl start heketi


これで構築完了です。



環境を確認するために、以下を実行します。

sudo heketi-cli topology load --json=/etc/heketi/topology.json

ここで

Adding device /dev/svdb ... Unable to add device: Setup of device /dev/svdb failed (already initialized or contains data?): Device /dev/svdb not found.

が出た場合、ブロックデバイスをフォーマット&マウントしてしまっている可能性があります(ていうか一度やらかした)。EBSならアタッチするだけで良いです。

Found node gluster1 on cluster 46cbd1c8cc061fc7eaa2be2a3656292d
Adding device /dev/xvdf ... OK

といった表示になれば成功。
同じコマンドを繰り返すと、一度成功しているノード/デバイスについては

Found node gluster1 on cluster 46cbd1c8cc061fc7eaa2be2a3656292d
Found device /dev/xvdf

のような表示になります。

また、

sudo heketi-cli topology info

で、各ノード上、どれだけの領域を確保し、どれだけ使用しているかがわかります。
時々、k8sからのプロビジョニング後、pod、pv、pvcを止めたときに解放されてないことがあり、再度領域確保しようとして足りない、なんてケースがあるので、このコマンドは結構使います。




EC2上の環境構築はここまで。


著者提供の学習環境について

学習環境1=Minikube、学習環境2=3台構成のkubernetesクラスター、学習環境3=IKS/GKEと3つも紹介されていますが、学習環境2だけ使いました。



なお、著者提供のVagrantベースでのクラスターは、全部Ubuntuで統一されてますし、基本的にすんなり動きます。
コンテナレジストリのところは手を入れる必要がありますがけど、それくらいですね。


「学習環境2」と呼ばれるマルチノード環境のを構築する上での推奨スペックが「Core i5」と記載されているのですが、「いつの世代のだよ」とツッコミつつ、恐る恐る手元の環境で起動させたら動きました。
手元の環境=Core i7 7500U 2.7GHz 2コア4スレッド(※第7世代)のノートPCで、メモリ16GB+SSD 1TBなノートPC
です。
これでk8s用とGlusterFS用の計7ノードが一応動きました。

ファンはガンガン回りまくり、温度も上がりまくりですけどね。


なお、このVagrantで構築されるオーバレイネットワークはFlannnelベースです。一方、前述のEC2上で構築する例はCalicoを使用しています。Calicoも設定しなければザルなので、どちらでも演習の上で問題はないです。
書籍中でも、Step15の中でCalicoの話も出てきます。


環境構築の話はここまでで、実際に演習に取り組んだ話は、後半に続く。