Closed20

TerraformでOracle Cloudにk8sクラスタをデプロイする

今更ながら、OCI(Oracle Cloud Infrastructure)のAlway Freeプランが改訂された事を知りました。

https://ascii.jp/elem/000/004/056/4056339/
https://docs.oracle.com/en-us/iaas/Content/FreeTier/freetier_topic-Always_Free_Resources.htm
https://blog.potproject.net/2021/06/01/oracle-cloud-kubernetes/

以前と比べると、VM.Standard.A1.Flex4 OCPU/24 GBを追加で使えるようになっているようです。

既にAlways Freeのものを以下のリポジトリで管理していたので、VM.Standard.A1.Flex4 OCPU/24 GB分のInstanceを新たに作成します。

作成した(している)コードは以下においてあります。

https://github.com/ymmmtym/terraform-cloud-oci

Ubuntu20.04やCentos8のARM shapeは未対応?で使用できないようでした。
なので、初めてOracle Linuxを使ってみます。

試しに既にデプロイしているVM.Standard.E2.1.Microのコードをもとに、shapeやimage source_idだけを変更して1台だけデプロイすると、早速エラーになりました。

Terraform v1.0.0
on linux_amd64
Initializing plugins and modules...
oci_core_instance.oracle01[0]: Creating...
╷
│ Error: 400-InvalidParameter 
│ Provider version: 4.36.0, released on 2021-07-21.  
│ Service: Core Instance 
│ Error Message: Invalid ShapeConfig: null (Cannot launch flexible instance without ShapeConfig) 
│ OPC request ID: 9c2a2cb5d6b9cf61fceaae254bb6eb95/05805FBC9837D61713DEA7755EC7976E/A505946AF66C8436E8F88CB815AAA916 
│ Suggestion: Please update the parameter(s) in the Terraform config as per error message Invalid ShapeConfig: null (Cannot launch flexible instance without ShapeConfig)
│ 
│ 
│   with oci_core_instance.oracle01[0],
│   on compute.tf line 44, in resource "oci_core_instance" "oracle01":44: resource "oci_core_instance" "oracle01" {
│ 
╵

Error Message: Invalid ShapeConfig: null (Cannot launch flexible instance without ShapeConfig)

Flexible instanceなので、shape_configが空であると怒られてしまいます。

以下を追加してリトライしました。

  shape_config {
    ocpus         = 1
    memory_in_gbs = 6
  }

次はデプロイがうまくいきました。一番上が今回作ったものになります。

OCI Instances

1台目のデプロイはうまくいったので、4台分デプロイしてみます。

$ git diff
diff --git a/terraform/compute.tf b/terraform/compute.tf
index 3688d2f..f97cc41 100644
--- a/terraform/compute.tf
+++ b/terraform/compute.tf
@@ -20,7 +20,7 @@ resource "oci_core_instance" "ubuntu" {
 }
 
 resource "oci_core_instance" "oracle_linux" {
-  count               = 1
+  count               = 4
   availability_domain = oci_core_subnet.subnet01.availability_domain
   compartment_id      = var.COMPARTMENT_OCID
   shape               = "VM.Standard.A1.Flex"

変更点は上記だけなので、mainブランチに直接pushしてしまいます。

Terraform v1.0.0
on linux_amd64
Initializing plugins and modules...
oci_core_instance.oracle_linux[3]: Creating...
oci_core_instance.oracle_linux[2]: Creating...
oci_core_instance.oracle_linux[1]: Creating...
╷
│ Error: 500-InternalError 
│ Provider version: 4.36.0, released on 2021-07-21.  
│ Service: Core Instance 
│ Error Message: Out of host capacity. 
│ OPC request ID: ec9f41ac843ad40d151603a4381a550d/78E8B02D020448FEBF521B56923CBD24/774E6C2486F80B0DBBBC0251A5922882 
│ Suggestion: The service for this resource encountered an error. Please contact support for help with service: Core Instance
│ 
│ 
│   with oci_core_instance.oracle_linux[1],
│   on compute.tf line 22, in resource "oci_core_instance" "oracle_linux":22: resource "oci_core_instance" "oracle_linux" {

Error Message: Out of host capacity. と言うメッセージが返ってきて、エラーになってしまいました。
こ、これは以前にもありましたが、単純にOCI側にリソースがなくてinstanceが作れていない状態です。

試しにGUIで作ると以下のような警告が出てきて作成する事ができません。

OCI Craete Instance Error by Lack of Resource

こればかりはOracle様がリソース追加してくれる事を、気長に待つしかないです

terraform apply
terraform destroy

を繰り返していたら、1台も作れなくなってしまいました。。。。。
どうやら大人気でリソースが少ないので、期間を置いて試してみます。

fishを使っているので、こんな感じで作成されるまで繰り返します。。。

set $status 1 # 何でもいいのでエラーコード1にする
while [ $status = 1 ]; terraform apply -auto-approve ;end

instance作成のtfファイルは出来たので、次はk8s周りの設定をしていきます。
現状の構成から以下の変更を加えようと思います。

  • NAT Gatewayは使っていない
  • PublicIPを持ったVM.Standard.E2.1.Microが2台ある
    • NAT,Bastion用途で使用する
    • Public SubnetとPrivateSubnetに分ける
  • PublicIPを持ったLBが1台ある
    • k8sクラスタにバランシングする

To Do

terraformでやること

  • Instance
    • bastion
      • SSH agent forwardingの許可 -> 不要でした
      • NAT
      • (Optional)iptablesの設計を決める -> とりあえず最低限のNAT設定のみ
      • (Optional) keepalived -> 使うならマネージドiLB
    • k8s
      • cloud-initでDockerをインストール
  • Network
    • Private / Public Subnetの分離
    • eLBのバックエンドセットをk8sクラスタに更新
    • (Optional)iLBのバックエンドセットをbastionに更新し、Public SegmentのGWにする
  • RKEのterraform

Reference

既にssh-agent-forwardingでログインできました。
恥ずかしながら、ログインユーザが間違ってました。
Oracle Linuxの場合は、デフォルトでopcでした。

ubuntu@ubuntu01:~$ ssh opc@oracle01.vcn01
The authenticity of host 'oracle01.vcn01 (192.168.0.4)' can't be established.
ECDSA key fingerprint is SHA256:yCZFVuomQ4Oh6UrbXi6fhA98K1HYF3hA+Lxl51O2YqQ.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'oracle01.vcn01' (ECDSA) to the list of known hosts.
Last login: Mon Jul 26 03:15:53 2021 from ubuntu02.vcn01.vcn01.oraclevcn.com
[opc@oracle01 ~]$ cat /etc/os-release 
NAME="Oracle Linux Server"
VERSION="7.9"
ID="ol"
ID_LIKE="fedora"
VARIANT="Server"
VARIANT_ID="server"
VERSION_ID="7.9"
PRETTY_NAME="Oracle Linux Server 7.9"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:oracle:linux:7:9:server"
HOME_URL="https://linux.oracle.com/"
BUG_REPORT_URL="https://bugzilla.oracle.com/"

ORACLE_BUGZILLA_PRODUCT="Oracle Linux 7"
ORACLE_BUGZILLA_PRODUCT_VERSION=7.9
ORACLE_SUPPORT_PRODUCT="Oracle Linux"
ORACLE_SUPPORT_PRODUCT_VERSION=7.9

https://docs.oracle.com/cd/E80594_01/dbcs_dbaas/CSDBI/GUID-70A912B7-9EAD-482B-A484-18F94A35356C.htm#CSDBI-GUID-70A912B7-9EAD-482B-A484-18F94A35356C

Subnetの分離

現在使用しているvcnには、PublicIPを持たないホストがいるため、以下のようにsubnetを分離してNATを利用し、PrivateIPしか持たないホストでも外部に通信ができるようにします。

  • vcn01: 192.168.0.0/16
    • Public Subnet: 192.168.0.0/24
      • Bastion用のインスタンスが2台存在
        • Ubuntu20.04
        • PublicIPを持っている
        • 外部からSSHができる
        • NAT設定する
    • Private Subnet: 192.168.1.0/24
      • k8sクラスタ用のインスタンスが1~4台存在
        • Oracle Linux 7.9
        • PrivateIPのみ持っている
        • デフォルトルートをPublic SubnetにあるNATのインスタンスに向ける

BastionサーバにNAT設定

PublicIPを持ったVM.Standard.E2.1.Micro(これ以降bastionと呼びます)にNATの設定を実施します。
cloud-initを使って起動時にコマンドを実行する設定をしました。
以下のようにPrivate Subnetをnatする事で、外部に疎通ができるようになりました。

  • ens3はinstanceのInterface
  • 192.168.1.0/24はPrivate Subnetのsegment
#cloud-config
# vim:syntax=yaml
runcmd:
- sudo sysctl -w net.ipv4.ip_forward=1
- sudo sed -i -e "s/^#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/g" /etc/sysctl.conf
- sudo iptables -F
- sudo iptables -I FORWARD 1 -i ens3 -o ens3 -j ACCEPT
- sudo iptables -t nat -A POSTROUTING -o ens3 -s 192.168.1.0/24 -j MASQUERADE

Oracle LinuxにDockerをインストール

スタンドアロンで出来る方法に絞って、Dockerをインストールしていきます。(そのほかに知っている方がいれば教えて頂きたいです。)

Rancherのスクリプトを使う

まずは、一番手軽な方法から試してみます。

https://rancher.com/docs/rancher/v2.x/en/installation/requirements/installing-docker/

以下のスクリプトを実行するだけになります。

curl https://releases.rancher.com/install-docker/20.10.sh | sh

実行しましたが、エラーとなってしまいました。

+ sh -c 'yum install -y -q docker-ce-cli-20.10.7-3.el7'
警告: /var/cache/yum/aarch64/7Server/docker-ce-stable/packages/docker-ce-cli-20.10.7-3.el7.aarch64.rpm: ヘッダー V4 RSA/SHA512 Signature、鍵 ID 621e9f35: NOKEY
docker-ce-cli-20.10.7-3.el7.aarch64.rpm の公開鍵がインストールされていません
Importing GPG key 0x621E9F35:
 Userid     : "Docker Release (CE rpm) <docker@docker.com>"
 Fingerprint: 060a 61c5 1b55 8a7f 742b 77aa c52f eb6b 621e 9f35
 From       : https://download.docker.com/linux/centos/gpg
+ sh -c 'yum install -y -q docker-ce-20.10.7-3.el7'
エラー: パッケージ: docker-ce-rootless-extras-20.10.7-3.el7.aarch64 (docker-ce-stable)
             要求: fuse-overlayfs >= 0.7
エラー: パッケージ: docker-ce-rootless-extras-20.10.7-3.el7.aarch64 (docker-ce-stable)
             要求: slirp4netns >= 0.4
 問題を回避するために --skip-broken を用いることができます。
 これらを試行できます: rpm -Va --nofiles --nodigest

aarch64と言うメッセージがあるように、arm64には対応していない?みたいです。

$ systemctl status docker 
Unit docker.service could not be found.

そんな時に、Oracle本家のブログ記事を見つけました。

https://blogs.oracle.com/linux/post/oracle-container-runtime-for-docker-on-oracle-linux-for-arm

yum install docker-engineだけすればdockerをインストールできるとのことです。

実行ログ
[opc@oracle01 ~]$ sudo yum install -y docker-engine
読み込んだプラグイン:langpacks, ulninfo
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ docker-engine.aarch64 0:19.03.11.ol-13.el7 を インストール
--> 依存性の処理をしています: runc >= 3:1.0.0-1.rc95 のパッケージ: docker-engine-19.03.11.ol-13.el7.aarch64
--> 依存性の処理をしています: containerd >= 1.4.8 のパッケージ: docker-engine-19.03.11.ol-13.el7.aarch64
--> 依存性の処理をしています: container-selinux >= 2:2.77 のパッケージ: docker-engine-19.03.11.ol-13.el7.aarch64
--> 依存性の処理をしています: docker-cli のパッケージ: docker-engine-19.03.11.ol-13.el7.aarch64
--> トランザクションの確認を実行しています。
---> パッケージ container-selinux.noarch 2:2.119.2-1.911c772.el7_8 を インストール
---> パッケージ containerd.aarch64 0:1.4.8-1.el7 を インストール
---> パッケージ docker-cli.aarch64 0:19.03.11.ol-13.el7 を インストール
---> パッケージ runc.aarch64 3:1.0.0-1.rc95.el7 を インストール
--> 依存性の処理をしています: criu のパッケージ: 3:runc-1.0.0-1.rc95.el7.aarch64
--> トランザクションの確認を実行しています。
---> パッケージ criu.aarch64 0:3.12-2.el7 を インストール
--> 依存性の処理をしています: libprotobuf-c.so.1(LIBPROTOBUF_C_1.0.0)(64bit) のパッケージ: criu-3.12-2.el7.aarch64
--> 依存性の処理をしています: libprotobuf-c.so.1()(64bit) のパッケージ: criu-3.12-2.el7.aarch64
--> 依存性の処理をしています: libnet.so.1()(64bit) のパッケージ: criu-3.12-2.el7.aarch64
--> トランザクションの確認を実行しています。
---> パッケージ libnet.aarch64 0:1.1.6-7.el7 を インストール
---> パッケージ protobuf-c.aarch64 0:1.0.2-3.el7 を インストール
--> 依存性解決を終了しました。

依存性を解決しました

=============================================================================================================================================
 Package                           アーキテクチャー        バージョン                                 リポジトリー                      容量
=============================================================================================================================================
インストール中:
 docker-engine                     aarch64                 19.03.11.ol-13.el7                         ol7_oci_included                  14 M
依存性関連でのインストールをします:
 container-selinux                 noarch                  2:2.119.2-1.911c772.el7_8                  ol7_oci_included                  39 k
 containerd                        aarch64                 1.4.8-1.el7                                ol7_oci_included                  22 M
 criu                              aarch64                 3.12-2.el7                                 ol7_latest                       430 k
 docker-cli                        aarch64                 19.03.11.ol-13.el7                         ol7_oci_included                  21 M
 libnet                            aarch64                 1.1.6-7.el7                                ol7_latest                        56 k
 protobuf-c                        aarch64                 1.0.2-3.el7                                ol7_latest                        27 k
 runc                              aarch64                 3:1.0.0-1.rc95.el7                         ol7_oci_included                 2.7 M

トランザクションの要約
=============================================================================================================================================
インストール  1 パッケージ (+7 個の依存関係のパッケージ)

総ダウンロード容量: 60 M
インストール容量: 276 M
Downloading packages:
(1/8): container-selinux-2.119.2-1.911c772.el7_8.noarch.rpm                                                           |  39 kB  00:00:00     
(2/8): criu-3.12-2.el7.aarch64.rpm                                                                                    | 430 kB  00:00:00     
(3/8): containerd-1.4.8-1.el7.aarch64.rpm                                                                             |  22 MB  00:00:02     
(4/8): docker-cli-19.03.11.ol-13.el7.aarch64.rpm                                                                      |  21 MB  00:00:02     
(5/8): libnet-1.1.6-7.el7.aarch64.rpm                                                                                 |  56 kB  00:00:00     
(6/8): protobuf-c-1.0.2-3.el7.aarch64.rpm                                                                             |  27 kB  00:00:00     
(7/8): runc-1.0.0-1.rc95.el7.aarch64.rpm                                                                              | 2.7 MB  00:00:00     
(8/8): docker-engine-19.03.11.ol-13.el7.aarch64.rpm                                                                   |  14 MB  00:00:01     
---------------------------------------------------------------------------------------------------------------------------------------------
合計                                                                                                          15 MB/s |  60 MB  00:00:03     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  インストール中          : 2:container-selinux-2.119.2-1.911c772.el7_8.noarch                                                           1/8 
  インストール中          : containerd-1.4.8-1.el7.aarch64                                                                               2/8 
  インストール中          : protobuf-c-1.0.2-3.el7.aarch64                                                                               3/8 
  インストール中          : libnet-1.1.6-7.el7.aarch64                                                                                   4/8 
  インストール中          : criu-3.12-2.el7.aarch64                                                                                      5/8 
  インストール中          : 3:runc-1.0.0-1.rc95.el7.aarch64                                                                              6/8 
  インストール中          : docker-cli-19.03.11.ol-13.el7.aarch64                                                                        7/8 
  インストール中          : docker-engine-19.03.11.ol-13.el7.aarch64                                                                     8/8 
xfs_info: cannot open /var/lib: ディレクトリです
  検証中                  : containerd-1.4.8-1.el7.aarch64                                                                               1/8 
  検証中                  : docker-cli-19.03.11.ol-13.el7.aarch64                                                                        2/8 
  検証中                  : docker-engine-19.03.11.ol-13.el7.aarch64                                                                     3/8 
  検証中                  : 2:container-selinux-2.119.2-1.911c772.el7_8.noarch                                                           4/8 
  検証中                  : criu-3.12-2.el7.aarch64                                                                                      5/8 
  検証中                  : 3:runc-1.0.0-1.rc95.el7.aarch64                                                                              6/8 
  検証中                  : libnet-1.1.6-7.el7.aarch64                                                                                   7/8 
  検証中                  : protobuf-c-1.0.2-3.el7.aarch64                                                                               8/8 

インストール:
  docker-engine.aarch64 0:19.03.11.ol-13.el7                                                                                                 

依存性関連をインストールしました:
  container-selinux.noarch 2:2.119.2-1.911c772.el7_8        containerd.aarch64 0:1.4.8-1.el7        criu.aarch64 0:3.12-2.el7              
  docker-cli.aarch64 0:19.03.11.ol-13.el7                   libnet.aarch64 0:1.1.6-7.el7            protobuf-c.aarch64 0:1.0.2-3.el7       
  runc.aarch64 3:1.0.0-1.rc95.el7                          

完了しました!
[opc@oracle01 ~]$ systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: https://docs.docker.com
[opc@oracle01 ~]$ sudo systemctl start docker
[opc@oracle01 ~]$ sudo systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
[opc@oracle01 ~]$ sudo usermod -a -G docker $USER
[opc@oracle01 ~]$ sudo docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

次回のログイン以降は、opcユーザにてsudo権限なしでdockerコマンドが実行できます。

無事にインストールする事ができましたので、上記をもとにcloud-init.ymlを作成しました。

#cloud-config
# vim:syntax=yaml
runcmd:
  - sudo yum install -y docker-engine
  - sudo systemctl start docker
  - sudo systemctl enable docker
  - sudo usermod -a -G docker opc # $USERは使えませんでした

instance作成後にdockerが動いている事を確認します。

[opc@oracle01 ~]$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

RKE Terraform

kubernetesの構築ツールにはRKEを利用します。
terraformのリソースをそのまま使える、以下のrkeプロバイダでtfファイルを記載していきます。(GitHubを参照)

https://registry.terraform.io/providers/rancher/rke/latest

実際にterraform applyすると成功し、kubernetesクラスタが構築されている事が確認できました。

[opc@oracle01 ~]$ docker container ls
CONTAINER ID        IMAGE                                                      COMMAND                  CREATED             STATUS              PORTS               NAMES
ecb9dc05036c        rancher/mirrored-coredns-coredns                           "/coredns -conf /etc…"   33 minutes ago      Up 33 minutes                           k8s_coredns_coredns-55b58f978-nd6sr_kube-system_2c696135-88d5-4d57-a80d-575c7507004e_0
69efd7e2704b        rancher/mirrored-nginx-ingress-controller-defaultbackend   "/server-arm64"          33 minutes ago      Up 33 minutes                           k8s_default-http-backend_default-http-backend-6977475d9b-bjc72_ingress-nginx_71be26d2-d8cf-4877-8005-81676346459e_0
c6f4259f6e91        rancher/mirrored-pause:3.2                                 "/pause"                 33 minutes ago      Up 33 minutes                           k8s_POD_coredns-55b58f978-nd6sr_kube-system_2c696135-88d5-4d57-a80d-575c7507004e_0
12bf3cc33185        rancher/mirrored-cluster-proportional-autoscaler           "/cluster-proportion…"   33 minutes ago      Up 33 minutes                           k8s_autoscaler_coredns-autoscaler-76f8869cc9-fz8jr_kube-system_6955d35e-c996-4264-ad43-e11fc4748baa_0
ecd0050bd189        rancher/nginx-ingress-controller                           "/usr/bin/dumb-init …"   33 minutes ago      Up 33 minutes                           k8s_nginx-ingress-controller_nginx-ingress-controller-tpjtr_ingress-nginx_e1408e4a-8a29-423d-bd24-ba7fcad9da99_0
67e45b609e7d        rancher/mirrored-pause:3.2                                 "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_default-http-backend-6977475d9b-bjc72_ingress-nginx_71be26d2-d8cf-4877-8005-81676346459e_0
4e6be2c0e612        rancher/mirrored-pause:3.2                                 "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_coredns-autoscaler-76f8869cc9-fz8jr_kube-system_6955d35e-c996-4264-ad43-e11fc4748baa_0
e65df82f92be        rancher/flannel-cni                                        "/install-cni.sh"        34 minutes ago      Up 34 minutes                           k8s_install-cni_kube-flannel-pmfcn_kube-system_3deb1082-3668-4ee6-907d-5fc45ce48981_0
806aee5c3b8f        rancher/mirrored-pause:3.2                                 "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_nginx-ingress-controller-tpjtr_ingress-nginx_e1408e4a-8a29-423d-bd24-ba7fcad9da99_0
a5cf72cb48b8        rancher/coreos-flannel                                     "/opt/bin/flanneld -…"   34 minutes ago      Up 34 minutes                           k8s_kube-flannel_kube-flannel-pmfcn_kube-system_3deb1082-3668-4ee6-907d-5fc45ce48981_0
bc5985899526        rancher/mirrored-pause:3.2                                 "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_kube-flannel-pmfcn_kube-system_3deb1082-3668-4ee6-907d-5fc45ce48981_0
7219c0d3886f        rancher/hyperkube:v1.20.8-rancher1                         "/opt/rke-tools/entr…"   35 minutes ago      Up 35 minutes                           kube-proxy
3beaac0fd85a        rancher/hyperkube:v1.20.8-rancher1                         "/opt/rke-tools/entr…"   35 minutes ago      Up 35 minutes                           kubelet
56f196ccf7d8        rancher/hyperkube:v1.20.8-rancher1                         "/opt/rke-tools/entr…"   35 minutes ago      Up 35 minutes                           kube-scheduler
b694deb67800        rancher/hyperkube:v1.20.8-rancher1                         "/opt/rke-tools/entr…"   35 minutes ago      Up 35 minutes                           kube-controller-manager
b31728e3221a        rancher/hyperkube:v1.20.8-rancher1                         "/opt/rke-tools/entr…"   35 minutes ago      Up 35 minutes                           kube-apiserver
737801e8a25b        rancher/rke-tools:v0.1.75                                  "/docker-entrypoint.…"   36 minutes ago      Up 36 minutes                           etcd-rolling-snapshots
ed16cec534e9        rancher/mirrored-coreos-etcd:v3.4.15-rancher1              "/usr/local/bin/etcd…"   36 minutes ago      Up 36 minutes                           etcd

ただし、local_fileを使ってkube_configを取得使用としましたが、実際には作られないようです。

$ terraform apply -auto-approve

# snip...

	# local_file.kube_cluster_yaml will be created
  + resource "local_file" "kube_cluster_yaml" {
      + content              = (sensitive)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "kube_config_cluster.yml"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.


local_file.kube_cluster_yaml: Creating...
local_file.kube_cluster_yaml: Creation complete after 0s [id=bc1d87d0049b08945c0404e75a0037271cfa831a]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

$ ls kube_cluster_yaml
ls: kube_cluster_yaml: No such file or directory

RKE Terraform

kubernetesの構築ツールにはRKEを利用します。
terraformのリソースをそのまま使える、以下のrkeプロバイダでtfファイルを記載していきます。(GitHubを参照)

https://registry.terraform.io/providers/rancher/rke/latest

実際にterraform applyすると成功し、kubernetesクラスタが構築されている事が確認できました。

[opc@oracle01 ~]$ docker container ls
CONTAINER ID        IMAGE                                                      COMMAND                  CREATED             STATUS              PORTS               NAMES
ecb9dc05036c        rancher/mirrored-coredns-coredns                           "/coredns -conf /etc…"   33 minutes ago      Up 33 minutes                           k8s_coredns_coredns-55b58f978-nd6sr_kube-system_2c696135-88d5-4d57-a80d-575c7507004e_0
69efd7e2704b        rancher/mirrored-nginx-ingress-controller-defaultbackend   "/server-arm64"          33 minutes ago      Up 33 minutes                           k8s_default-http-backend_default-http-backend-6977475d9b-bjc72_ingress-nginx_71be26d2-d8cf-4877-8005-81676346459e_0
c6f4259f6e91        rancher/mirrored-pause:3.2                                 "/pause"                 33 minutes ago      Up 33 minutes                           k8s_POD_coredns-55b58f978-nd6sr_kube-system_2c696135-88d5-4d57-a80d-575c7507004e_0
12bf3cc33185        rancher/mirrored-cluster-proportional-autoscaler           "/cluster-proportion…"   33 minutes ago      Up 33 minutes                           k8s_autoscaler_coredns-autoscaler-76f8869cc9-fz8jr_kube-system_6955d35e-c996-4264-ad43-e11fc4748baa_0
ecd0050bd189        rancher/nginx-ingress-controller                           "/usr/bin/dumb-init …"   33 minutes ago      Up 33 minutes                           k8s_nginx-ingress-controller_nginx-ingress-controller-tpjtr_ingress-nginx_e1408e4a-8a29-423d-bd24-ba7fcad9da99_0
67e45b609e7d        rancher/mirrored-pause:3.2                                 "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_default-http-backend-6977475d9b-bjc72_ingress-nginx_71be26d2-d8cf-4877-8005-81676346459e_0
4e6be2c0e612        rancher/mirrored-pause:3.2                                 "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_coredns-autoscaler-76f8869cc9-fz8jr_kube-system_6955d35e-c996-4264-ad43-e11fc4748baa_0
e65df82f92be        rancher/flannel-cni                                        "/install-cni.sh"        34 minutes ago      Up 34 minutes                           k8s_install-cni_kube-flannel-pmfcn_kube-system_3deb1082-3668-4ee6-907d-5fc45ce48981_0
806aee5c3b8f        rancher/mirrored-pause:3.2                                 "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_nginx-ingress-controller-tpjtr_ingress-nginx_e1408e4a-8a29-423d-bd24-ba7fcad9da99_0
a5cf72cb48b8        rancher/coreos-flannel                                     "/opt/bin/flanneld -…"   34 minutes ago      Up 34 minutes                           k8s_kube-flannel_kube-flannel-pmfcn_kube-system_3deb1082-3668-4ee6-907d-5fc45ce48981_0
bc5985899526        rancher/mirrored-pause:3.2                                 "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_kube-flannel-pmfcn_kube-system_3deb1082-3668-4ee6-907d-5fc45ce48981_0
7219c0d3886f        rancher/hyperkube:v1.20.8-rancher1                         "/opt/rke-tools/entr…"   35 minutes ago      Up 35 minutes                           kube-proxy
3beaac0fd85a        rancher/hyperkube:v1.20.8-rancher1                         "/opt/rke-tools/entr…"   35 minutes ago      Up 35 minutes                           kubelet
56f196ccf7d8        rancher/hyperkube:v1.20.8-rancher1                         "/opt/rke-tools/entr…"   35 minutes ago      Up 35 minutes                           kube-scheduler
b694deb67800        rancher/hyperkube:v1.20.8-rancher1                         "/opt/rke-tools/entr…"   35 minutes ago      Up 35 minutes                           kube-controller-manager
b31728e3221a        rancher/hyperkube:v1.20.8-rancher1                         "/opt/rke-tools/entr…"   35 minutes ago      Up 35 minutes                           kube-apiserver
737801e8a25b        rancher/rke-tools:v0.1.75                                  "/docker-entrypoint.…"   36 minutes ago      Up 36 minutes                           etcd-rolling-snapshots
ed16cec534e9        rancher/mirrored-coreos-etcd:v3.4.15-rancher1              "/usr/local/bin/etcd…"   36 minutes ago      Up 36 minutes                           etcd

ただし、local_fileを使ってkube_configを取得使用としましたが、実際には作られないようです。

$ terraform apply -auto-approve

# snip...

	# local_file.kube_cluster_yaml will be created
  + resource "local_file" "kube_cluster_yaml" {
      + content              = (sensitive)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "kube_config_cluster.yml"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.


local_file.kube_cluster_yaml: Creating...
local_file.kube_cluster_yaml: Creation complete after 0s [id=bc1d87d0049b08945c0404e75a0037271cfa831a]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

$ ls kube_cluster_yaml
ls: kube_cluster_yaml: No such file or directory

本来であれば、kubectl --kubeconfig /path/to/config_yaml get nodesのように使うので、現状kubectlコマンドが(簡単には)使えない事になります。

Kubernetes Provider

RKEにてk8sクラスタの構築は終わったので、TerraformのKubernetes Providerを使用してmanifestをapplyさせていきます。

https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#config_context
provider "kubernetes" {
  host                   = rke_cluster.cluster.api_server_url
  username               = rke_cluster.cluster.kube_admin_user
  client_certificate     = rke_cluster.cluster.client_cert
  client_key             = rke_cluster.cluster.client_key
  cluster_ca_certificate = rke_cluster.cluster.ca_crt
}

resource "kubernetes_namespace" "example" {
  metadata {
    name = "my-first-namespace"
  }
}

しかし、rke_clusterでbastionの設定をしていたのであっさりとエラーが出ました。

kubernetes_namespace.example: Creating...
kubernetes_namespace.example: Still creating... [10s elapsed]
kubernetes_namespace.example: Still creating... [20s elapsed]
kubernetes_namespace.example: Still creating... [30s elapsed]
╷
│ Error: Post "https://192.168.1.122:6443/api/v1/namespaces": dial tcp 192.168.1.122:6443: i/o timeout
│ 
│   with kubernetes_namespace.example,
│   on kubernetes.tf line 10, in resource "kubernetes_namespace" "example":10: resource "kubernetes_namespace" "example" {
│ 
╵

kubernetes providerにはbastion機能がないため、GitHub Actionsから直接k8sのAPIのエンドポイントを叩く事ができません。

  • bastionのiptablesでportforwardingの設定を追加 <- このやり方を試してみます。
    • DHCPなので名前解決が必要
      • dns-utilsを入れる
      • k8sクラスタが起動した後に実行する必要がある(bastion作成時にはIPアドレス付与されていないため)
  • OCI内部のホスト(bastionかk8s master node)でkubectlを実行する
    • rkeからのリソースを渡しにくくなる
    • kubernetes provider使えない

k8sクラスタ作成時に、bastionでiptablesを実行

  • k8sクラスタのinstance作成時に、terraform remote-execでbastionにSSHしてiptablesを実行しました

    • この時、k8s masterノードのPrivateIPの情報を引き渡す事ができます。
  • この状態で再度実行します。

kubernetes_namespace.example: Creating...
╷
│ Error: Post "https://140.238.54.52:6443/api/v1/namespaces": x509: certificate is valid for 192.168.1.25, 127.0.0.1, 10.43.0.1, not 140.238.54.52
│ 
│   with kubernetes_namespace.example,
│   on kubernetes.tf line 9, in resource "kubernetes_namespace" "example":9: resource "kubernetes_namespace" "example" {
│ 
╵

今度は証明書のが有効でないのでAPIが叩けませんでした。

https://github.com/docker/machine/issues/531

証明書を自作して、PublicIPからのアクセスを許可すれば通りそうですが、

それ以外にも問題があったので、いったんこの構成の検証はここまでにします。

構成変更

構成変更に至ったきっかけ

always freeプランで無料で使う事が前提ですが、課金プランに登録しました。

それにより、少しだけ認識が間違っていたので料金が発生してしまったり、、制約が緩くなったものもあるので、これをきっかけに構成変更します。

  • Block Volumeのfreeで使える容量の認識を誤っていた
    • 合計で200GB
    • 1 instanceには自動で50GB使用される
    • つまり無料で使用できるinstanceは4つまで -> bastion廃止
  • Ubuntu20.04が使用できないと勘違いしていた
    • (あまりよく知らない)Oracle Linuxで代用していた
    • minimalは使用できないが、普通のものは使える
  • PublicIPを使用できる数が増えた
    • 課金プランに変更した(使用するのは無料分だけなので、もちろん無料)
    • 各instanceにPublicIPを割り当てられる

料金表は以下に載っています。

https://www.oracle.com/jp/cloud/price-list.html

新しい構成

  • Public Subnetにkubernetesクラスタを全て置く
    • kubernetesクラスタは全てエフェメラルPublicIPを持つ
    • bastionは使用しない

terraform provider rke

以下のように、nodeのaddressをPublicIPに設定します。

resource "rke_cluster" "cluster" {
  nodes {
    address          = oci_core_instance.kubernetes.0.public_ip # instance作成時に指定したリソース名
    internal_address = oci_core_instance.kubernetes.0.private_ip # instance作成時に指定したリソース名
    user             = "ubuntu"
    ssh_key          = var.PRIVATE_KEY_INSTANCE
    role             = ["controlplane", "worker", "etcd"]
  }

この状態でterraform applyしましたが、以下のエラーに

time="2021-07-31T13:02:55Z" level=info msg="[controlplane] Successfully started Controller Plane.."time="2021-07-31T13:02:55Z" level=info msg="[authz] Creating rke-job-deployer ServiceAccount"
│ 
│ Failed running cluster err:Failed to apply the ServiceAccount needed for job execution: Post "https://132.226.8.71:6443/apis/rbac.authorization.k8s.io/v1/clusterrolebindings?timeout=30s": dial tcp 132.226.8.71:6443: connect: no route to host========================================

実際にinstanceにログインしてcurlを叩いてみると、確かにno route to hostと返ってきます。
デフォルトでiptablesに設定が入っていたため、iptablesは全て削除してからリトライします。

sudo iptables -F

次のエラー

│ 
│ Failed running cluster err:[network] Can't access KubeAPI port [6443] on Control Plane host: 132.226.8.71
│ ========================================
│ 
│ 
│   with rke_cluster.cluster,
│   on rke_cluster.tf line 7, in resource "rke_cluster" "cluster":7: resource "rke_cluster" "cluster" {
│ 
╵

instanceにログインして確認すると、実際にはconnection timeoutとなってしまいます。
疎通も取れて、sshも出来ています。。

$ curl 132.226.8.71:6443
curl: (28) Failed to connect to 132.226.8.71 port 6443: Connection timed out

$ ping 132.226.8.71
PING 132.226.8.71 (132.226.8.71) 56(84) bytes of data.
64 bytes from 132.226.8.71: icmp_seq=1 ttl=63 time=0.219 ms

$ ssh 132.226.8.71 "hostname"
ubuntu01

セキュリティールールでは、egress,ingress共に6443を許可していますが、試しにセキュリティルールを全て許可してリトライしてみます。また、iptablesに以下の設定を追加して、内部からPublicIPにAPIを投げるときに、PrivateIPになるようにアドレスを変換しました。

# PublicIP(132.226.10.44)をPrivateIP(192.168.0.211)に変換する
sudo iptables -t nat -A OUTPUT -d 132.226.10.44 -j DNAT --to-destination 192.168.0.211

リトライしましたが、同様のエラーとなってしまいました。
実際にinstanceにログインしてcurlを叩いてみると、アドレス変換は行われているようでアクセスできました。

ubuntu@ubuntu01:~$ curl https://132.226.10.44:6443
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to 132.226.10.44:6443 

しかしながら、instance外部からAPIを実行してみると、timeoutしてしまいます。
どうやらrkeでクラスタを構築するときは、外部からAPIで通信できる必要がありそうです。

iptablesの設定と、セキュリティールールの設定は全てoffにしておりますが、
おそらくOracle Cloud側で制御されていそうな気がしますが、もう少し調査してみます。

instanceの調査

まずは、nginxのdockerをinstanceの80 Portで公開します。

ubuntu@ubuntu01:~$ docker run -d --rm -p 80:80 nginx:alpine
Unable to find image 'nginx:alpine' locally
alpine: Pulling from library/nginx
fd3acdcea568: Pull complete 
c1670e91f443: Pull complete 
7fdac1642bfa: Pull complete 
1394476d63eb: Pull complete 
ad901a1137d5: Pull complete 
fe0fe785c479: Pull complete 
Digest: sha256:bead42240255ae1485653a956ef41c9e458eb077fcb6dc664cbc3aa9701a05ce
Status: Downloaded newer image for nginx:alpine
f62a20c8b82ebb29580c358d63cd9989988909c9c67e8d37511c2bcdc8b4a246
ubuntu@ubuntu01:~$ ss -antu | grep 0.0.0.0:80
tcp   LISTEN     0      4096                0.0.0.0:80            0.0.0.0:*
ubuntu@ubuntu01:~$ curl localhost:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

無事に起動できており、localhostでアクセスできる事が確認できます。

次に、instance外部からこのPublicIP:80にアクセスできるか確認してみます。

$ curl http://132.226.10.44:80
curl: (7) Failed to connect to 132.226.10.44 port 80: Operation timed out

timeoutが返ってきました。

どうやら、instance側のiptables設定がよろしくなさそうなので、もう一度正しくiptablesなどの設定します。クラスタ作成に必要なポートは、以下のrancherドキュメントに載っています。

https://rancher.com/docs/rke/latest/en/os/

ブリッジを通過するトラフィックの処理を許可します。

sudo sysctl -w net.bridge.bridge-nf-call-iptables=1

iptablesの設定をします。

とりあえず、instance側のデフォルトの設定は全てACCEPTにします。
(必要あればセキュリティーリスト側で制御しようと思います。)

sudo iptables -F
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo netfilter-persistent save

以上でリトライしてみます。

local_file.kube_cluster_yaml: Creating...
local_file.kube_cluster_yaml: Creation complete after 0s [id=b66d01bef1609dcdf6255eb34578a8348a513dad]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

クラスタ作成する事ができました!!

ただ、local_fileでkubeconfigのymlが作成される設定にしていますが、作成されていないようです。
(PublicIPのクラスタを作成する場合は、local_fileで作成できないのでしょうか、、、)

kubernetesリソースの作成

以下のようなHCIを用意して、terraform applyします。

provider "kubernetes" {
  host                   = rke_cluster.cluster.api_server_url
  username               = rke_cluster.cluster.kube_admin_user
  client_certificate     = rke_cluster.cluster.client_cert
  client_key             = rke_cluster.cluster.client_key
  cluster_ca_certificate = rke_cluster.cluster.ca_crt
}

resource "kubernetes_namespace" "example" {
  metadata {
    name = "my-first-namespace"
  }
}

kuberctlが使えないので、etcdで見てみます。

ubuntu@ubuntu01:~$ docker container ls | grep etcd
41be522b5ed9   rancher/rke-tools:v0.1.75                                  "/docker-entrypoint.…"   13 minutes ago   Up 13 minutes             etcd-rolling-snapshots
4fd1a3a6df0b   rancher/mirrored-coreos-etcd:v3.4.15-rancher1              "/usr/local/bin/etcd…"   13 minutes ago   Up 13 minutes             etcd
ubuntu@ubuntu01:~$ docker container exec -it 4fd1 sh
# etcdctl get / --prefix --keys-only | grep my-first-namespace
/registry/configmaps/my-first-namespace/kube-root-ca.crt
/registry/namespaces/my-first-namespace
/registry/secrets/my-first-namespace/default-token-g4rtz
/registry/serviceaccounts/my-first-namespace/default

とりあえず、無事に作られてはいそうでした。
kubectlが使えないのがしんどいですが、kubernetesリソースもterraform管理して運用していきたいと思います。

冪等性について

2回目にterraform applyすると、kubernetesのリソースで以下のエラーになりました。

╷
│ Error: Get "http://localhost/api/v1/namespaces/my-first-namespace": dial tcp 127.0.0.1:80: connect: connection refused
│ 
│   with kubernetes_namespace.example,
│   on kubernetes.tf line 9, in resource "kubernetes_namespace" "example":9: resource "kubernetes_namespace" "example" {
│ 
╵

おそらく、rke_clusterのtfstateがうまく保持できていないからだと思います。

terraform state rm で強制的にkubenetes_namespaceを削除して、再実行してみると

kubernetes_namespace.example: Creating...
╷
│ Error: namespaces "my-first-namespace" already exists
│ 
│   with kubernetes_namespace.example,
│   on kubernetes.tf line 9, in resource "kubernetes_namespace" "example":9: resource "kubernetes_namespace" "example" {

tfstateだけ削除しても、kubenetesにはリソースがいると怒られてしまいました。

後で気づきましたが、このnamespaceを消すためには、tfファイルの記載を削除してterraform applyする必要がありました。

kubectlの導入

local_fileで作成されないのは、おそらくterraformの実行先をterraform cloudにしており、API drivenで実行している事が関係していると思います。(多分)

またkubectlが使えなくとも、kubeconfigが参照できないのはしんどいので、以下を実施することにしました。

  • masterにkubectlをインストール
  • masterに~./kube/configを配置

terraform rke_clusterのtfファイルに、以下のprovisionerを追記しました。

  provisioner "remote-exec" {
    connection {
      type        = "ssh"
      user        = "ubuntu"
      host        = oci_core_instance.kubernetes.0.public_ip
      private_key = var.PRIVATE_KEY_INSTANCE
    }
    scripts = [
      "provisioning/install-kubectl.sh",
    ]
  }
  provisioner "file" {
    connection {
      type        = "ssh"
      user        = "ubuntu"
      host        = oci_core_instance.kubernetes.0.public_ip
      private_key = var.PRIVATE_KEY_INSTANCE
    }
    content = rke_cluster.cluster.kube_config_yaml
    destination = "~/.kube/config"
  }

install-kubectl.shの中身は以下のようになっており、バイナリ形式でインストールしてます。

curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/arm64/kubectl" # arm64に設定
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
mkdir -p ~/.kube # 最後に~/.kubeディレクトリを作成して、後続のfile provisionerで~/.kube/configを配置できるようにしています。

無事にrke_clusterが完了したので、instanceにログインしてkubectlを実行してみます。

ubuntu@ubuntu01:~$ kubectl  get nodes
NAME              STATUS   ROLES                      AGE   VERSION
152.70.98.146     Ready    controlplane,etcd,worker   83s   v1.20.8
168.138.199.247   Ready    worker                     80s   v1.20.8

無事にkubectlを実行する事ができました。

https://github.com/ymmmtym/terraform-cloud-oci

上記で管理できたため Close します。

次は、以下のようなことを検証したいと思います。

  • kubernetes の manifest をどのように運用するか
    • HCL or yaml(argocdなど)
  • ingress 導入時の LoadBalancer 作成
このスクラップは2021/08/10にクローズされました
ログインするとコメントできます