0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【EC2】【kubeadm】様々なツールを使ってk8sを導入した(所要1時間)

Posted at

はじめに

様々なツールを使ってEC2上へk8sを導入しました。
以下に手順(README.md)、コードをアップロードしています。

https://github.com/not75743/k8s_madeby_packer_terraform_ansible_kubeadm

~1時間で作成可能であるため、k8sを試して見たい方は覗いてみてください。

今回の記事では手順作成でつまずいた箇所、注目すべき箇所をメモしたいと思います。

※本投稿では手順の詳細を記載しません。
詳細については上記のリンクよりREADME.mdを参照ください。

作れるもの

  • k8sクラスタ
    • master *1
    • worker *1~3

構成図は以下となります。

EC2_kubernetes.png

対象読者

  • とりあえずk8sを試してみたい。
  • ローカル環境にサーバがないから利用したい。
  • ツール(packer,ansible,terraform)の使い方について参考にしたい

クラスタ作成の流れ

ざっくり以下になります

  1. 環境を用意する
  2. packerでk8sノードのイメージ作成
  3. terraformでAWS環境にリソース作成
  4. ansibleでmasterノード用意
  5. ansibleでworkerノード用意

※ 3~5はシェルスクリプトを使ってほぼ自動で実施するようにしています。

この項目ごとにポイントをピックアップしていきたいと思います。
今回はkubeadmでクラスタを用意しており、各手順で以下を参考にしました。

https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/

1.環境を用意する

もろもろのツールをインストールします。すでにあれば不要です。

dockerfile

dockerfileを用意しているため、用意に環境を準備可能です。
centos7をベースイメージとしています。

2.packerでk8sノードのイメージ作成

master,workerの共通AMIを作成するためにpackerを使用します。
AMIを作っておくことで2回目以降のクラスタ作成時間を短縮することが可能です。
AMI構築にはAnsible Remote Rrovisionerを利用しました。
https://www.packer.io/docs/provisioners/ansible/ansible

AWS用のクレデンシャルが不足する

packer build実行時、以下の様な出力が出ました。

no valid credential sources for found

no valid credentialとある通り、AWS用のcredentialが読み込めていません。
ローカルにcredentialを残さないため、今回は環境変数でcredentialを設定します。

export AWS_ACCESS_KEY_ID="xxxxxxxxxxxxxxxxxx"
export AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxx"

参考は以下
https://www.packer.io/docs/builders/amazon#authentication

sftpエラーとなる

packer build実行中にansibleが実行できなかった旨のログが出力されました。

amazon-ebs.centos: fatal: [kubenode]: FAILED! => {"msg": "failed to open a SFTP connection (EOF during negotiation)"}

Ansible Provisionerはリモートサーバ上で/usr/lib/sftp-server -eを実行し、ローカルとsftpでやり取りします。
CentOS7のベースAMIでは上記パスにsftpが存在しないため、以下の様にパスを書き換える必要があります。

main.pkr.hcl
sftp_command = "/usr/libexec/openssh/sftp-server -e"

参考は以下
https://www.packer.io/docs/provisioners/ansible/ansible#sftp_command
https://www.packer.io/docs/provisioners/ansible/ansible#redhat-centos

packer -v が出力されない

packer -v を実行してもバージョンが出力されず、出力待ちでプロンプトが停止しました。
原因は以下のpackerを実行しているからでした

/usr/sbin/packer

実態はcracklib-packerというパスワード正常性チェッカーでした。
今回は使わないのでdockerfileビルド時にシンボリックリンクを削除しています。

$ ll /usr/sbin/packer
lrwxrwxrwx. 1 root root 15 12月 29  2020 /usr/sbin/packer -> cracklib-packer

参考は以下
https://qiita.com/0xmks/items/2513745d927941869d64
https://www.kabegiwablog.com/entry/2018/02/02/090000
https://github.com/cracklib/cracklib/issues/7

3.terraformでAWS環境にリソース作成

k8sノードを含めたAWSリソース作成にterraformを使用しました。
terraformを使うことで容易な環境作成、破棄が可能となります。

outputをansibleのhostsファイルに記載

(正確にはterraformでなくshellですが)本手順ではEC2のpublic ipを取得するためにmain.tf内でoutputを設定し、それを各ファイルにsedで転記しています。
「sedがipアドレス追記に使える!」と思いスクリプトに反映させましたが、調べてみるとほかにも手段がありそうなので、今後調べてみたいです。
EC2 inventory source
PRODUCE AN ANSIBLE INVENTORY WITH TERRAFORM

セキュリティグループは指定IPのみ許可

セキュリティグループは以下の様に接続元IPを記載したIPのみに絞ります。

resource "aws_security_group_rule" "worker_ssh" {
  type              = "ingress"
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
  cidr_blocks       = var.ssh_to_worker
  security_group_id = aws_security_group.kube_worker-sg.id # ここ
}

terraform.tfvarsでホストのIPを忘れずに記載しましょう。

ノードの数を指定

workerのEC2の個数を1~3にするために以下のようにコードを書きました。

resource "aws_instance" "worker" {
  count                       = (var.worker_count == 0 ? 1 : (var.worker_count < 4 ? var.worker_count : 3)) # ここ

いわゆるif文です。
入れ子にし、0,4以上を選択した場合にそれぞれ1,3に置き換える様にしています。
0だとk8sの意味がなくなる、4以上は結構な課金がされる、ということを考慮して書きました。

見づらい(気がする?)ため、もしほかにやり方が合ったらご教授いただけると幸いです。

参考は以下
https://www.terraform.io/docs/language/expressions/conditionals.html

4.ansibleでmasterノード用意

terraformで用意したノードにSSH接続し、ansibleでkubeadm initを実行します。

--apiserver-cert-extra-sans={{ masterip }}

k8sのapiserverにSAN(Subject Alternative Name)を追加します。
オプションなしkubeadm initで生成されるapiserver証明書のSANにはバブリックIPの記載がなく(CommonNameにもなく)、バブリックIP指定での接続が不可であるため本設定を追加します。

[root@ip-10-0-1-100 ~]# openssl x509 -text -noout -in /etc/kubernetes/pki/apiserver.crt | grep -A1 "X509v3 Subject Alternative Name:"
            X509v3 Subject Alternative Name:
                DNS:ip-10-0-1-100.ap-northeast-1.compute.internal, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, IP Address:10.96.0.1, IP Address:10.0.1.100, IP Address:3.113.29.168 # ここの一番後ろに追加されている

参考は以下
https://stackoverflow.com/questions/46360361/invalid-x509-certificate-for-kubernetes-master
https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/
https://kubernetes.io/ja/docs/setup/best-practices/certificates/

get token & token-ca-cert-hash

それぞれworkerがk8sに参加するkubeadm join 時に必要になります。
masterのplaybook実行時にそれぞれ拾い、それをlineinfileモジュールで変数ファイルに記載し、workerのplaybookで参照できるようにします。

master.yaml
    - name: get token
      shell: kubeadm token list | awk 'NR==2{print $1}'
      register: token

    - name: replace worker var "token"
      lineinfile:
        path: ./vars/vars.yaml
        regexp: "^token:"
        line: "token: {{ token.stdout }} "
        state: present
      delegate_to: localhost
      become: no

lineinfileはローカルで実行するためdelegate_toでローカルを選択します。

参考は以下
https://qiita.com/fumiya-konno/items/d2d6b67296e5ee94340b
https://docs.ansible.com/ansible/latest/user_guide/playbooks_delegation.html#id3

kubeconfig作成

k8sのAPIサーバと通信するためにkubeconfigをローカルに持ってきます。
持っていく先は~/.kube/configにすると、kubectl時に自動で反映されるため楽です。
すでにk8sを使っているユーザの場合、既存kubeconfigを上書きしてしまうと困るため、今回は作業ディレクトリ内にコピーし、そのパスを環境変数KUBECONFIGで指定するようにしています。

参考は以下
https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/_print/
https://kubernetes.io/ja/docs/concepts/configuration/organize-cluster-access-kubeconfig/#kubeconfig%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0

5.ansibleでworkerノード用意

masterのplaybookが終了したらworkerのplaybookを実行します。
とはいってもkubeadm joinを実行するだけです。

playbook実行時の「Are you sure you want to continue connecting」を防ぐ

masterもworkerも初めてSSH接続するノードであるため、playbook実行前に接続するか問われます。

The authenticity of host 'xxxx.com (xxx.xxx.xxx.xxx)' can't be established.
RSA key fingerprint is xx:xx:xx:xx...
Are you sure you want to continue connecting (yes/no)?

ansibleで接続できなくなるため、cfgファイルにhost_key_checking = Falseを記載し、playbook実行前に環境変数で指定します。

export ANSIBLE_CONFIG=./cfg/ansible.cfg
ansible.cfg
[defaults]
host_key_checking = False # to avoid ssh check

参考は以下
https://docs.ansible.com/ansible/latest/reference_appendices/config.html#host-key-checking

おわりに

大変でしたが、1から作って各コンポーネントの理解を深められたと思います。
以下の様な課題がまだあるため、これから手を付けていきたいところです。

  • packer,terraform,ansibleでファイル、ディレクトリ構成をあまり意識していなかった(roleで分ける等)

  • kubeadm initで何しているのか追い切れていない。

  • terraform destroy を忘れると課金が止まらない。(監視の仕組みを作りたい)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?