Kubernetesのカスタマイズポイントのまとめ

※ この記事は2019年03月23日に別のサイトで公開していたものをコピーしたもので、最新のKubernetesに対応している保証はありません

インフラエンジニアがKubernetesを使って自社の基盤を設計する上で気になるのは「Kuberneretesが自社の要件にフィットするのかどうか」です。 そういった時のためにKubernetesは拡張可能なポイントを数多く備えています。 この記事では現時点で最新のKubernetes v1.13のカスタマイズ可能なポイントを整理して自社の要件に必要な機能をKubernetesに持たせることができるのかどうかを考えるための情報をまとめます。

なお、この記事で扱うのはAPIリクエストのカスタマイズの方法であり、ネットワークやストレージまわりのカスタマイズは対象としません。 通信のパフォーマンスやデータのバックアップ、リストアについて検討されている方はCNI(Container Network Interface)CSI(Container Storage Interface)のドキュメントをご覧ください。個人的にはCSIを使った実装の一つであるRookに注目しています。

KubernetesAPIリクエストのフロー

まず、カスタマイズをする前のKubernetesの基本的な処理の流れを確認します。KubernetesはRESTfullなAPIで設計されており、Masterで立ち上がっているAPI ServerがKubernetesの各種リソースに対するリクエストを受け付けています。

Kubernetesアーキテクチャはとてもシンプルで、ユーザがkubectlを実行すると対象のリソースの操作内容に対応するAPI Serverのエンドポイントにリクエストが送られます。 API Serverの役割はリクエストのヘッダを確認して認証と認可を実施したあと、受け取ったリクエストをetcd上に保存することです。Kuberbernetesは各種リソースに対応するControllerがetcdを監視しており、コンテナの作成やServiceの作成、変更などの実際のアクションはControllerがetcd上のデータの追加や変更を検知して実行します。 このように、あるべき姿を定義した上でControllerが状態の変化を監視し、変化があれば対応する処理を実施することをReconciliation loopと呼びます。

カスタマイズ機能の分類

Kubernetesにどんなことがカスタマイズできるのかを調べるということは、ユーザがリクエストを送ってからetcdにリソースの定義を書き込むまでの間にどのような情報を付与するか、またはオリジナルのリソースとそれに対応するControllerを定義するか、あるいはそのどれでもない完全に独自の仕組みを作るかに分類できます。

それぞれの選択肢には明確な違いがあり、API Serverのリクエストを補完する拡張やAPI ServerのAPIエンドポイントを増やす拡張、Kubernertesが想定する枠組みをほとんど使わない方法があります。

大まかに分類すると以下の通りです。

手法 説明
Admission Controller API Serverに対するリクエストの認証。認可を済ませたあとにそのリクエストをハンドルする拡張。様々な種類がある。どの拡張を実施するかはKubernetesクラスタ構築時に指定する
Custom Resource Definition API Serverに新しい種類のリソースを登録する。リソースの定義や振る舞いは独自に定義できる。クラスタ構築後でもサービスを停止することなく追加可能
Aggregated API Server k8sAPI Serverの基本プロトコルでは実現できない処理を実現するための専用のAPI Serverを別途用意する拡張

なお、最近のKubernetesの流行として押さえておくべきトピックとしてOperatorがあります。OperatorはCustom Resorce Definition(通称CRD)のうち、特定のアプリケーション、ミドルウェアに特化したものです。Operatorは特定のツールを抽象化するための強力な仕組みです。

例えば、ビッグデータのインメモリ分散処理フレームワークApache SparkがKubernetesをサポートしたすぐ後にGoogleApache SparkのためのOperatorを公表しました。Spark Operetorがデプロイされたk8sクラスタであれば、以下のマニフェストを使ってKubernetesにリクエストを投げることでSparkジョブを実行することができます。 

apiVersion: sparkoperator.k8s.io/v1beta1
kind: SparkApplication
metadata:
  name: spark-pi
  namespace: default
spec:
  type: Scala
  mode: cluster
  image: gcr.io/spark/spark:v2.4.0
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.11-2.4.0.jar

もしもKubernetes上で特定のアプリ、ミドルウェアを使うことがあるのであれば、対応するOperatorが公開されているかを調べてみるのもいいかもしれません。

ただし、Operatorは想定した動作を簡単なyamlを使って実行するための仕組みです。普段の作業をテンプレートに落とし込めるので正常系の処理は楽になりますが、障害が発生したときはそのアプリやミドルに詳しい人が必要であることには変わらないので、Operatorがあれば運用の全てが解決するわけではないことは忘れないようにしましょう。 例えばorg.apache.spark.examples.SparkPi以外のSparkアプリケーションを実行すると毎回SparkのMasterのPodがOOMで死ぬといった場合はOperatorが対処できる範囲外で、spark.driver.memoryを見直してDriverに割り当てるメモリを増やすなどSparkの知識を必要とするチューニングが必要になります。

Admission Controller

Admisstion Controllerは最もシンプルで簡単なカスタマイズ手法です。 ユーザからリクエストを受けとったAPI Serverが認証と認可を済ませた後に、そのリクエストに含まれるパラメータを差し替えたり認可とは別に実行そのものを許可するかどうかを制御することができます。 Kubernetesがあらかじめ用意している複数のポリシーから選択してAPI Serverに設定することができます。

Kubernetes v1.13時点では以下がデフォルトで設定されています。

NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeClaimResize,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,Priority

API Server起動時に--enable-admission-pluginsオプションにカンマ区切りで書くことで他のAdmission Controllerの追加や削除をすることができます。

Admission Controllersは数が多いのでここでは個別の説明は省きます。 詳細を知りたい方は以下のリンク先にそれぞれの説明がありますので参照してください。

https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/

Custom Resource Definition

Custom Resource Definition(CRD)はAPI Serverに新規のエンドポイントを登録するための仕組みです。新しいリソースの定義をCRDとして登録すると、API Serverにそのリソースのエンドポイントを新規に追加することができます。

CRDだけではAPI Serverにリクエストのエンドポイントを追加することしかできません。追加したリソースの挙動はそのリソースのCRDに対応するCustom Resource Controllerが決定します。 このControllerは自分で実装した上でKubernetesクラスタ上にデプロイする必要があります。 デプロイされたContollerは独自のロジックに従い対応するリソースに対してReconciliation loopを実行していきます。

Custom Resource DefinitionとCustom Resource Controllerを実装するためのSDKの例として以下があります。

  • kubebuilder
  • Operator SDK
  • Metacontroller

余談ですが、通常だと1つのリソースに対して1つのControllerが存在するのですが、PodやDeploymentなどデフォルトで利用可能なリソースのControllerはKubernetesクラスタ構築時にkube-controller-managerという名前で1つのPodとしてデプロイされています。

Aggregated API Server

Aggregated API ServerはKubernetesの標準コンポーネントであるAPI Serverではなく新しく用意する別のServerで処理を実現するための仕組みです。 API Serverはユーザから受け取ったリクエストを処理する機能を持つことはこれまで説明してきましたが、実はその処理を他のServerを委譲する抽象化レイヤを持っています。 デフォルトのAPI Server以外にユーザから受け取ったリクエストを処理するAPI ServerをAggregated API Serverといいます。 Aggregated API Serverの例としてmetrics serverやservice catalogがあります。

Aggregated API Serverが登録されているAPI Serverはユーザのリクエストの認証と認可を済ませると該当する処理をAggregated API Serverに丸投げします。 Aggregated API Serverは受け取ったリクエストを内部で処理し、結果をAPI Serverと通じてユーザに返します。 Custom Resource DefinitionではAPI Serverが全ての処理を実施した上でReconcilation loop処理を実施していましたが、Aggregated API Serverではさらにその前でhookすることが可能になるため自由度はCustom Resource Definitionよりも高いです。

Aggregated API Serverを実装するためのSDKとしてapiserver-builderが用意されています。

カスタマイズ手法の比較

手法 追加実装 拡張の自由度の高さ
Admission Controller 不要 低い
Custom Resource Definition 必要 ある程度制約があるが比較的自由
Aggregated API Server 必要 ほとんど制約がなく自由

導入を検討する場合はまず最初に使えるAdmission Controllerがないかを調査し、必要な機能が不足していると感じればCustom Resource Definitionを検討し、それでも駄目ならAggregated API Serverを採用する順番がおすすめです。 自由度の高さはそのまま運用コストと利用方法を普及するコストの増加につながりますので、運用者が少ない場合はできるだけAdmission Controllerの利用に留めるのが正解です。

資料

【サイバーエージェント&ニフクラ】春の Kubernetes 祭り! @銀座 にて上に書いている内容を図にしてまとめて発表しました。

スライドはSpeakerDeckにて公開しておりますのでぜひそちらもご覧ください。

Kubernetesのカスタマイズポイントのまとめ

Docker Meetup #27参加報告

Docker Meetuo#27の参加報告

ざっと見て19:00の段階で8割の席は埋まっている

KubeCon 報告1: コンテナランタイムやFirecrackerの話題ひととおり振り返ってみよう (Container runtimes & Firecracker Recap) Kohei Tokunaga

  • 2018年はコンテナランタイム群雄割拠の年
  • KubeConで出てきたコンテナランタイムに関する話題を全て紹介する
  • k8sランタイムとセキュリティ
    • コンテナ内からの脅威とコンテナ外からの脅威に分けられる
    • コンテナ内からの攻撃は現実には存在しないが、そのうち発生してくると予想されている
    • 何をすればいいか
      • 脆弱性をなくすこと
      • 権限に制限を加える
      • コンテナが影響を与える範囲を狭める
    • これらはランタイムによって実現可能
    • Kubernetesセキュリティチームは脆弱性があってもgithubのissueで報告してはいけないというルールになっている
    • dockerdは拡張性の高さが特徴
    • cri-o
    • podman
      • 軽量かつrootlessなのが特徴
    • kata containers VS gVisor
      • kata
        • Pod単位のVMでコンテナを隔離
        • VMと名前があるが、runc並みにCPU, RAM, NW性能が高い
      • gVisor
        • ユーザ空間でカーネルを再実装している
        • NWはKataの半分程度の性能しかでないため、現実世界のアプリケーションでのパフォーマンスは低め
    • kata VS Nable
      • Nabla
        • 7種類のシステムカールのみを発行しており、バグを踏みにくいと主張
    • Firecrackerのミーティングが場外で行われたので参加してきた
      • 軽量VMでコンテナを隔離
      • 現状ではOCIやCRIには準拠していないが、Kataやcontainerdと統合してKubernetes上で使えるようになりつつある
      • containerdのプラグインの一つとして振る舞う
  • 感想
    • 自分の環境にどのランタイムが判断できる人ってどれだけいるんだろう

KubeCon 報告2: About kanister Makoto Hasegawa

  • 資料: https://speakerdeck.com/makocchi/kubecon-cncon-2018-recap-about-kanister
  • 管理者からみるとランタイムは動かしてみると面白いよ
  • Kanisterの話をする
  • CI/CD, Kubernetes, and Databases: Better Togethe の話
  • Cloud Nativeな環境でDBを動かすのは難しい
    • アプリと隔離されており、ペットとして扱われている(家畜じゃないよ、ペットだと、という意味)
    • DB専属のエンジニアが必要になってくる
  • 全部 CI/CDに組み込んで自動化できれば幸せになるはず
  • Kanister(燃料缶の意味)があればそれができるよとのこと
    • data capster/export
      • SCI対応でblock storageなどにファイルをdumpできるし、S3にアップロードもできる
    • database manipulation
      • fileterやマスキングか可能になる予定
    • CRDを使った処理などKubernetesと密接したDBのコントロールが可能
    • kanisterが対応しているもの
    • kanister blueprintが肝だと思っている
      • taskを定義したCRDで、コミュニティに広がれば誰もが恩恵を得られるようになる
  • Kanisterはhelmでインストールできる
  • productionクラスタとtestクラスタそれぞれでDBを用意して、cloud storageを介してデータをやり取りしながら検証するデモ
    • 異なるnamespaceに同じ内容のDBを構築したりすることもできるようになる

Docker Compose-on-Kubernetes Kunal Kushwaha

  • docker-composeの役割の紹介
  • compose on kubernetesの話へ
  • docker stackコマンドでkubernetesにデプロイするデモ
  • 感想
    • dockerだけを使っていた人があとからkubernetesに移行するときに便利
    • Daplyomentのサポートはあるけど、Jobのサポートがあるか気になる

DockerのCUIツール作った話 @gorilla0513

  • 資料: https://docs.google.com/presentation/d/1ty9MbOGT8HThYVC1DVZtmhKS5MyCNGkDdkCTs6vpqzk/edit#slide=id.p
  • こんなのを作った:https://github.com/skanehira/docui
  • 初心者の人に便利な機能を用意したかった
  • 作ったモチベーション
    • Goを勉強したい, コマンド操作がめんどくさい、ターミナルから抜けたくない
  • 作ったとき大変だったこと
    • Dockerは業務で使っていなかったので詳しくなかった
    • cuiライブラリのgocuiも詳しくない
    • docker apiって何か知らなかった
    • 一人で開発していて相談できる相手がいなかった
  • 人に使ってもらえることや形になるまで作りたくてモチベーションを維持していった
  • 開発にしてくれる人を募集中
  • 感想
    • 熱意で業務に関係がないツールを作りきったのすごい

CNAB: Cloud Native Application Bundle Kohei Ota

  • CNAB: 分散アプリをパッケージングする企画
    • MicrosoftとDocker者が主体となって開発を進めている
  • Cloud Nativeはいろいろコンポーネントがあり、それぞれの学習コストが高い
  • CNABの基本構成
  • bundle,json: CNABのバージョンやイメージ等
  • app/Dockerfile:
  • run:
  • DuffleというCLIでCNABで管理しているアプリを操作する
    • goで書いてるのにgo getじゃ落ちてこないなどハードルが高め
  • Docker-appもCNABをサポートしている
    • https://github.com/docker/app
    • 2018年にDocker社がリリースしたエコシステム
    • docker-composeだと環境ごとの差分を扱うのが大変なので用意されたもの
  • Duffleのdemo
    • DuffleのREADME.mdに書かれているコマンドを順番に実行
  • 感想
  • 分散アプリと聞いてHadoopみたいなものの管理ツールかと思ったけれど、単にKubernetesのパッケージングツール?
  • 今回のデモの範囲だとhelmとの違いがよくわからなかった

LT 1: GitLab Serverlessとその中身(KubeCon 報告LT) Takuya Noguchi

  • AWS Lambdaがあるけどベンダ非依存でサーバレスを使いたいよねという人向けにKnativvveが出てきた
  • Gitlab ServerlessはKubecon Seattle中にalpha版としてリリース
  • Knative
  • Istioを含むヘビーな子
  • RedHat, アイビーエムgあサポート開始
  • Gilab Serverlessを使う手順
  • TrigerMesh CLI
    • tmコマンド で操作

LT 2: kubectlを実行してからetcdに書き込むまでに @yosshi_

LT 3:Rancher’s New Mluti-Tenant Prometheus Support Yutaka Ichikawa

  • 資料: https://qiita.com/cyberblack28/items/8a1dbd26aef8a85e6b8e
  • コンテナベースオーケストレーションの本の6章を担当、増刷決まりました
  • KubeConではMySQL OperatorやVittesが面白かった
  • Rancher 2.2で組み込まれるPrometheusの話をする
  • Prometheusはalpha版
  • カタログの機能としてGrafanaとPrometheusをそれぞれカタログが用意されていたが、それらのデプロイがさらに簡単になった
  • Monitoringというボタンを押して、Prometheusを選択してSaveするだけ
  • 各種方法は公式のBlogに書いてあるので読んでね

LT 4: JKDv18.12でCFP採択までに考えたこと Ryoma Fujiwara

  • 大きいイベントで話をしたいと考えてる人の背中を後押しするのが目的の発表
  • 考えたこと
  • 求められていることはなにか
  • 実感、共感を持ってもらう話をするには?
  • 求められていること
    • 例えば個別技術の細かい話だが、自分はOSSのメンテナでもないので、無理だと思った
    • そこで、沿い文の取り組みを知ってもらったり、何かを得てもらい、行動や判断にいい影響を与えたい
    • ユーザ企業にしか話せないことを考え、プロダクト開発で技術的にぶつかった課題やみんなが直面するであろう課題について話しをすることにした
  • 実感、共感を持ってもらう話をするには?
    • キラキラした話は実感をもってもらいにくい
    • そこで、実際に問題にぶつかった人に話してもらうのがいいと思い、いろんな担当者に登壇してもらうことにした
  • 発表することになると、正しいことを話すために調べて新しい知識を得られる
  • 予告: CloudNativeDays TokyoのCFPを近いうちに募集します

ラズパイクラスタにKubernetesをインストール(成功編)

「ラズパイクラスタにKubernetesをインストール(失敗編)」でCentOSを選んだためDockerのインストールに失敗したので、今度はUbuntuで試してみる。

今回の作業内容

全体の作業概要

Dockerをインストールしようとすると依存関係のエラーが出て、その解決方法を調べるのに時間がとられた。

OSイメージをMicroSDに焼く

UbuntuのwikiからOSイメージをダウンロードする。

公式イメージはPi2用しか配布されていないのでPi3にインストールするためには一手間が必要になる。一応Unofficialとして個人が公開しているPi3用のイメージへのリンクがwikiに貼られている。

いろいろ面倒そうなので公式イメージを使うことにした。

ubuntu-18.04.1-preinstalled-server-armhf+raspi2.img.xzMacにダウンロードしてxzコマンドで解凍する。

$ xz -d ubuntu-18.04.1-preinstalled-server-armhf+raspi2.img.xz 
$ ls
ubuntu-18.04.1-preinstalled-server-armhf+raspi2.img

MicroSDカードをUnMountしてからSDカードへ書き込む。1枚書き込むのにだいたい30分かかる。

$ diskutil unmountDisk /dev/disk2
Unmount of all volumes on disk2 was successful
$ sudo dd bs=1m if=./ubuntu-18.04.1-preinstalled-server-armhf+raspi2.img of=/dev/disk2
2252+0 records in
2252+0 records out
2361393152 bytes transferred in 1949.493602 secs (1211285 bytes/sec)

このOSイメージはPi2用なので、Pi3用で動かすためには

  • dbtファイルの置き換え
  • config.txtの書き換え

が必要。

また、Pi3 B+で動かすためにはさらにbootloader用にbootcode.bin, fixup.dat and start.elfを置き換える必要がある。

dbtファイルを含めて必要なファイルはGithubraspberrypi/firmwareリポジトリで公開されている。

wgetでダウンロードする。

$ wget https://github.com/raspberrypi/firmware/raw/master/boot/bcm2710-rpi-3-b-plus.dtb
$ wget https://github.com/raspberrypi/firmware/raw/master/boot/bootcode.bin
$ wget https://github.com/raspberrypi/firmware/raw/master/boot/fixup.dat
$ wget https://github.com/raspberrypi/firmware/raw/master/boot/start.elf

MicroSDカードが/Volume/system-bootにマウントされているとして、ダウンロードしたファイルをコピーする

$ cp bcm2710-rpi-3-b-plus.dtb /Volumes/system-boot/
$ cp start.elf /Volumes/system-boot/
$ cp bootcode.bin /Volumes/system-boot/
$ cp fixup.dat /Volumes/system-boot/

次に、/Vomue/system-boot/config.txtの中身を以下のように変更する。

# For more options and information see
# http://www.raspberrypi.org/documentation/configuration/config-txt.md
# Some settings may impact device functionality. See link above for details

# コメントアウト
#kernel=uboot.bin
#device_tree_address=0x02000000

# 追記
kernel=vmlinuz
initramfs initrd.img followkernel

終わったらSDカードを取り出す。

$ diskutil eject /dev/disk2
Disk /dev/disk2 ejected

OSをインストールする

MicroSDカードを刺した状態で電源をいれるとOSのインストールが始まる。

ログインプロンプトが表示されたら

アカウント名:ubuntu

パスワード: ubuntu

でログインしてすぐにパスワードの初期化を求められるので実施する。

  • ホスト名の設定: hostname XXX

続いて、ネットワークの設定をしてパッケージを更新する。

  • eth0にアドレスを割り当てる

eth0へのネットワークの割り当てはsudo dhclient eth0でできる。

続いて、パッケージを更新する。

  • sudo apt update
  • sudo apt upgrade -y
    • 途中、keyboad-configurationの設定を聞かれる.

上記を3台に実施する。

ホスト名とIPアドレス、それぞれの役割は以下の通り。

ホスト名 IPアドレス 役割
compute-0-0 192.168.10.109 KubernetesのMaster
compute-0-1 192.168.10.108 KubernetesのNode1
compute-0-2 192.168.10.107 KubernetesのNode2

このままだとネットワークの名前解決ができないので、各ホストの/etc/hostsに以下の内容を追記する。

192.168.10.109 compute-0-0
192.168.10.108 compute-0-1
192.168.10.107 compute-0-2

Dockerのインストール

インストールしたUbuntuはarmhf(32bit版)として動作している。docker-ceの推奨は64bitだがUbuntuであればarmhfをサポートしているので問題ない。

$ sudo apt-get update

$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# armv7lなのでarmhfアーキ用を実行
$ sudo add-apt-repository \
   "deb [arch=armhf] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

$ sudo apt-get update

$ sudo apt-get install docker-ce

$ docker --version
Docker version 18.09.0, build 4d60db4

ubuntuユーザがdockerコマンドを実行するたびにsudoが必要である。

sudoがなくてもdockerコマンドを実行できるようにubuntuユーザをdockerグループに追加しておく。

$ sudo gpasswd -a ubuntu docker
Adding user ubuntu to group docker
$ sudo systemctl restart docker

なお、依存関係のエラーでdocker-ceのインストールに失敗することがあった。

その場合はapt-utilの依存関係を修復する。

$ sudo apt-get install docker-ce
Reading package lists... Done
Building dependency tree       
Reading state information... Done
You might want to run 'apt --fix-broken install' to correct these.
The following packages have unmet dependencies:
 apt-utils : Depends: apt (= 1.6.3) but 1.6.6 is to be installed
 docker-ce : Depends: docker-ce-cli but it is not going to be installed
             Depends: containerd.io but it is not going to be installed
             Recommends: aufs-tools but it is not going to be installed
             Recommends: cgroupfs-mount but it is not going to be installed or
                         cgroup-lite but it is not going to be installed
             Recommends: pigz but it is not going to be installed
             Recommends: libltdl7 but it is not going to be installed
E: Unmet dependencies. Try 'apt --fix-broken install' with no packages (or specify a solution).

$ sudo apt --fix-broken install
Reading package lists... Done
Building dependency tree       
Reading state information... Done
Correcting dependencies... Done
The following additional packages will be installed:
  apt-utils
The following packages will be upgraded:
  apt-utils
1 upgraded, 0 newly installed, 0 to remove and 89 not upgraded.
16 not fully installed or removed.
Need to get 0 B/192 kB of archives.
After this operation, 0 B of additional disk space will be used.
Do you want to continue? [Y/n] Y
...

修復が完了したらもう一度docker-ceをインストールする。

$ sudo apt-get install docker-ce
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  aufs-tools cgroupfs-mount containerd.io docker-ce-cli libltdl7 pigz
The following NEW packages will be installed:
  aufs-tools cgroupfs-mount containerd.io docker-ce docker-ce-cli libltdl7
  pigz
0 upgraded, 7 newly installed, 0 to remove and 89 not upgraded.
1 not fully installed or removed.
Need to get 29.7 MB of archives.
After this operation, 149 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y
...

Kubernetesをインストールする

Kubernetesは1.8からSwapが有効になっているとkubeletが起動しなくなっている。 今回の環境はSwap領域は存在しないので気にしてくていいが、もしSwapが有効になっていれば無効化が必要。

# swap領域が0になっている
$ free -h
              total        used        free      shared  buff/cache   available
Mem:           923M        138M         84M        4.4M        701M        764M
Swap:            0B          0B          0B
# 念のためSwapの有無を確認
$ swapon -s

続いて、公式ドキュメントに従って全ホストでkubeadm, kubelet, kubectlをインストールする。

# rootになる
sudo su -
apt-get update && apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

インストールしたパッケージのバージョンを確認する。

# dpkg -l |grep kube
ii  kubeadm                          1.13.1-00                          armhf        Kubernetes Cluster Bootstrapping Tool
ii  kubectl                          1.13.1-00                          armhf        Kubernetes Command Line Tool
ii  kubelet                          1.13.1-00                          armhf        Kubernetes Node Agent
ii  kubernetes-cni                   0.6.0-00                           armhf        Kubernetes CNI

続いて、Kubernetesクラスタのネットワークを設計する。

Kubernetesはoerlay networkを構築する必要がある。そのためのツールとして有名なところではflannnel, weave, Calicoなどがある。

今回はARMプロセッサの上で動くUbuntu 18.04の32bit版で動作することが要件になる。

Calicoの要件AMD64プロセッサであること。Ubuntuの32bitで動作するか不安なので見送る。

WeaveはARMをサポートしたバイナリをリリースしているものの、32bitに対応しているか明記されていなかったこととWeaveをラズパイ3B+で動かしたときにカーネルに起因するバグがあるissueを見つけたので見送る。

そこで、今回はflannelを採用することにした。

先ほど書いたようにホストは192.168.10.0/24で設定されている。

flannelが環境構築のために公開しているマニフェストは初期値が10.2544.0.0/16になっている。Pod IPのCIDRは10.244.0.0/16を割り当てることにした。違うCIDRを割り当てたい場合は、後述するflannelの環境構築時にgithub上のマニフェストを指定するのではなく、ローカルにダウンロードしたマニフェストを編集して利用すればいい。

KubernetesのMasterを起動するため、compute-0-0でkubeadm initを実行する。

$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16
[init] Using Kubernetes version: v1.13.1
[preflight] Running pre-flight checks
[preflight] The system verification failed. Printing the output from the verification:
KERNEL_VERSION: 4.15.0-1017-raspi2
CONFIG_NAMESPACES: enabled
CONFIG_NET_NS: enabled
CONFIG_PID_NS: enabled
CONFIG_IPC_NS: enabled
CONFIG_UTS_NS: enabled
CONFIG_CGROUPS: enabled
CONFIG_CGROUP_CPUACCT: enabled
CONFIG_CGROUP_DEVICE: enabled
CONFIG_CGROUP_FREEZER: enabled
CONFIG_CGROUP_SCHED: enabled
CONFIG_CPUSETS: enabled
CONFIG_MEMCG: enabled
CONFIG_INET: enabled
CONFIG_EXT4_FS: enabled
CONFIG_PROC_FS: enabled
CONFIG_NETFILTER_XT_TARGET_REDIRECT: enabled (as module)
CONFIG_NETFILTER_XT_MATCH_COMMENT: enabled (as module)
CONFIG_OVERLAY_FS: enabled (as module)
CONFIG_AUFS_FS: enabled (as module)
CONFIG_BLK_DEV_DM: enabled
DOCKER_VERSION: 18.09.0
OS: Linux
CGROUPS_CPU: enabled
CGROUPS_CPUACCT: enabled
CGROUPS_CPUSET: enabled
CGROUPS_DEVICES: enabled
CGROUPS_FREEZER: enabled
CGROUPS_MEMORY: missing
    [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 18.09.0. Latest validated version: 18.06
error execution phase preflight: [preflight] Some fatal errors occurred:
    [ERROR SystemVerification]: missing cgroups: memory
[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`

cgroupのmemoryが無効化されているらしい。/proc/cgroupsを確認するとたしかにmemoryのenabledが0(無効)になっている。

$ cat /proc/cgroups 
#subsys_name    hierarchy   num_cgroups enabled
cpuset  10  2   1
cpu 6   60  1
cpuacct 6   60  1
blkio   7   60  1
memory  0   68  0
devices 4   60  1
freezer 2   2   1
net_cls 3   2   1
perf_event  5   2   1
net_prio    3   2   1
pids    8   66  1
rdma    9   1   1

boot時の設定を変更してcgroupfsのmemoryを有効にする。

/boot/firmware/cmdline.txtcgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memoryを追記して再起動する。追記が終わったあとのファイルは以下の通り。

$ cat /boot/firmware/cmdline.txt 
net.ifnames=0 dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory

# reboot

再起動したあとはeth0に割り当てられたIPが消えているのでsudo dhclient eth0を実行する。

memoryサブシステムが有効になっているのがわかる。

$ cat /proc/cgroups 
#subsys_name    hierarchy   num_cgroups enabled
cpuset  2   1   1
cpu 5   34  1
cpuacct 5   34  1
blkio   4   34  1
memory  7   68  1
devices 11  34  1
freezer 9   1   1
net_cls 3   1   1
perf_event  8   1   1
net_prio    3   1   1
pids    6   39  1
rdma    10  1   1

再度Kubernetesをインストールしなおす.

$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16
[init] Using Kubernetes version: v1.13.1
[preflight] Running pre-flight checks
    [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 18.09.0. Latest validated version: 18.06
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Activating the kubelet service
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [ubuntu kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.10.109]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [ubuntu localhost] and IPs [192.168.10.109 127.0.0.1 ::1]
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [ubuntu localhost] and IPs [192.168.10.109 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[kubelet-check] Initial timeout of 40s passed.
[apiclient] All control plane components are healthy after 85.513804 seconds
[uploadconfig] storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.13" in namespace kube-system with the configuration for the kubelets in the cluster
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "ubuntu" as an annotation
[mark-control-plane] Marking the node ubuntu as control-plane by adding the label "node-role.kubernetes.io/master=''"
[mark-control-plane] Marking the node ubuntu as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: ntvqxo.ghxfq7gvi8rc79aw
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstraptoken] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

  kubeadm join 192.168.10.109:6443 --token ntvqxo.ghxfq7gvi8rc79aw --discovery-token-ca-cert-hash sha256:709766a97f016e2a0fd5113b12ce5afa1cebc740ecfa6596f8bbe9f30cb5897f

kubectlに認証設定を読み込ませるために指定されたコマンドを実行する。

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

動作していることが確認できる。

$ kubectl get node
NAME          STATUS   ROLES    AGE   VERSION
compute-0-0   NotReady   master   23m   v1.13.1

STATUSがNodeReadyなのはOveray Networkが設定されていないから。flannelをインストールする。

$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.extensions/kube-flannel-ds-amd64 created
daemonset.extensions/kube-flannel-ds-arm64 created
daemonset.extensions/kube-flannel-ds-arm created
daemonset.extensions/kube-flannel-ds-ppc64le created
daemonset.extensions/kube-flannel-ds-s390x created

# STATUSがReadyになった
$ kubectl get node
NAME          STATUS   ROLES    AGE   VERSION
compute-0-0   Ready    master   34m   v1.13.1

Nodeになるcompute-0-1とcompute-0-2で以下を実行する。

$ sudo kubeadm join 192.168.10.109:6443 --token ntvqxo.ghxfq7gvi8rc79aw --discovery-token-ca-cert-hash sha256:709766a97f016e2a0fd5113b12ce5afa1cebc740ecfa6596f8bbe9f30cb5897f 
[preflight] Running pre-flight checks
    [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 18.09.0. Latest validated version: 18.06
[discovery] Trying to connect to API Server "192.168.10.109:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://192.168.10.109:6443"
[discovery] Requesting info from "https://192.168.10.109:6443" again to validate TLS against the pinned public key
[discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "192.168.10.109:6443"
[discovery] Successfully established connection with API Server "192.168.10.109:6443"
[join] Reading configuration from the cluster...
[join] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet] Downloading configuration for the kubelet from the "kubelet-config-1.13" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Activating the kubelet service
[tlsbootstrap] Waiting for the kubelet to perform the TLS Bootstrap...
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "compute-0-1" as an annotation

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the master to see this node join the cluster.

compute-0-0からkubectl get nodeを実行するとNodeがクラスタとして認識されていることを確認できる。

$ kubectl get node
NAME          STATUS   ROLES    AGE   VERSION
compute-0-2   Ready    <none>   73s   v1.13.1
compute-0-1   Ready    <none>   75s   v1.13.1
compute-0-0        Ready    master   39m   v1.13.1

以上でKubernetesクラスタの構築ができました。

CentOSのコンテナを作ってみる。

$ kubectl run tmp-centos --rm -it --image centos -- bash
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
[root@tmp-centos-cc49649b6-kpfvg /]# 

Deploymentが動くことも確認できた。

まとめ

Kubernetesクラスタのインストールは何度もやったことがあったので、初のラズパイといってもまぁすぐに終わるだろうと思っていたら、実家のネットワーク設定によるものやSDカードの不具合によるもの、OSの選定、OSインストール後の謎の個体差などなどでかなり時間がかかってしまった。

他にも実はここに書ききれない細かなトライ&エラーを繰り返している。Ubuntu 18系を使うのは初めてだったので、原因がOSのバージョン、カーネルのバージョン、ラズパイのどれに起因するものなのかの切り分けが大変だった。

トラブルの原因を調べて解決することを繰り返すことで知識がつくので、これはこれで新年からいい経験ができたと思う。

参考サイト

ラズパイクラスタにKubernetesをインストール(失敗編)

この記事はラズパイでKubernetesクラスタを構築する(物理編)の続きにあたる。

OSの選定を間違ってしまい作業が詰んだので失敗編としてまとめることにした。

正しい手順はラズパイクラスタにKubernetesのインストール(成功編)にまとめる。

OSとDockerのインストール

ラズパイ初心者なのでインストール可能なOSを一通り把握しておきたい。

Raspberry Pi で動く様々なOS一覧まとめ13種類!という2018.11.19の記事を読んでベーシックなRaspbianと普段業務で使っているCentOSで迷ったが、今後実際に使っていくことを考えるとKubernetesとして利用事例が多いCentOSを採用することにした。(そしてこれが失敗編を書くことになった理由である)

OSイメージをダウンロードする。 ラズパイのCPUはarmなので候補の一覧からarm向けを調べCentOS-Userland-7-armv7hl-RaspberryPI-Minimal-1810-sda.raw.xzというラズパイ向けのminimalイメージをダウンロードした。 

解凍にはxzコマンドが必要。brewでインストールする。

$ brew install xz

解凍する。

$ xz -d CentOS-Userland-7-armv7hl-RaspberryPI-Minimal-1810-sda.raw.xz 
$ ls
CentOS-Userland-7-armv7hl-RaspberryPI-Minimal-1810-sda.raw

続いて、このイメージファイルをddコマンドでMicroSDカードに書き込む。

USB Type-C ハブを使ってMacMicroSDカードを接続。

認識したディスクをdiskutilで調べると/dev/disk2で認識されている。

$ diskutil list
/dev/disk0 (internal):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                         500.3 GB   disk0
   1:                        EFI EFI                     314.6 MB   disk0s1
   2:          Apple_CoreStorage Macintosh HD            499.3 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3

/dev/disk1 (internal, virtual):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                  Apple_HFS Macintosh HD           +499.0 GB   disk1
                                 Logical Volume on disk0s2
                                 EF1D93DE-2460-4A71-B9DD-9C2223020689
                                 Unlocked Encrypted

/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *31.5 GB    disk2
   1:             Windows_FAT_32                         31.5 GB    disk2s1

SDカードをUnMountしてからSDカードへ書き込む。1枚書き込むのにだいたい40分かかる。

$ diskutil unmountDisk /dev/disk2
Unmount of all volumes on disk2 was successful
$ sudo dd bs=1m if=./CentOS-Userland-7-armv7hl-RaspberryPI-Minimal-1810-sda.raw of=/dev/disk2
2712+0 records in
2712+0 records out
2843738112 bytes transferred in 2440.917174 secs (1165029 bytes/sec)

# ディスクの取り出し
$ diskutil eject /dev/disk2
Disk /dev/disk2 ejected

なお、購入したUSB Type-C ハブは接続中Wifiが使えなくなったので、調べ物をしたくなるたびにケーブルを抜く必要があった。

配線済みのラズパイクラスタにOSを書き込んだSDカードを差し込む。しかしディスプレイに何も表示されない。どうやら電源ケーブルを刺した状態で後からSDカードを差し込んでも認識しないらしい。一度USBを抜いてもう一度刺し直すと認識しだした。

しばらくするとログインプロンプトが出てくる。

初期アカウント: root

初期パスワード: centos

でログインする。

続いて以下を実行。

  • 各種パッケージをアップデート: yum update -y
  • 日本語配列を受け付けるように変更: localectl set-keymap jp106
  • 初期パスワードを変更: passwd
  • ホスト名の設定: hostname XXX

df -hを実行すると32GBのMicroSDを使っているにもかかわらずCentOSは2.7GB程度しか認識していない。

dffdisk -lで確認すると、/dev/mmcblk0MicroSDカード本体で、これが3つのパーティション/dev/mmcblk0p1,/dev/mmcblk0p2,/dev/mmcblk0p3で区切られており、この3つの合計が2.7GBほどだった。

ここで問題になったのは/がマウントしている/dev/mmcblk0p3には1.4GBしか割り当てられておらず、そのうち1.2GBがすでに使われてしまっていること。このままではKubernetesをインストールできない。

そんな時、/root/READMEに参考になる情報があると書かれているサイトを見つけた。

開いてみると以下の内容であった。

# cat /root/README
== CentOS 7 userland ==

If you want to automatically resize your / partition, just type the following (as root user):
rootfs-expand

どうやらパーティションを拡張するためのコマンドが用意されているらしい。

manを実行しても何もでなかったのでrootfs-expand --helpと適当に叩くとオプションが無視されそのまま実行されてしまい慌てたものの、実行が完了すると/が28GBに拡張された。

つづいて、Dockerをインストールする。

Dockerの公式ドキュメントでCentOS向けのインストールガイド通りに従いdocker-ce用のリポジトリを追加してyumでインストールしようとするとhttps://download.docker.com/linux/centos/7/armhfp/stable/repodata/repomd.xml: [Errno 14] HTTPS Error 404 - Not Foundが返ってくる。

armhfp向けのパッケージが存在しないようだ。

代わりに、ラズパイ公式サイトの手順に従い以下のコマンドでインストールする。

curl -sSL https://get.docker.com | sh

すると、今度はEither your platform is not easily detectable or is not supported by this installer scrypt.のエラーになった。

Docker CEのドキュメントを見ると、CentOS向けのDocker CEはARM64 / AARCH64をサポートしているがARM(32bit版)をサポートしていないとのこと。

どうやらOS選定の段階で詰んでしまった様子。

OSを32bitでも対応しているDebian系(Rassbianとか)に変えるか、CentOSにこだわるのであればラズパイで動かせる非公式の64bit版のOSイメージをいれるしかない(一応見つけることはできた)。

どうにかならないかと調べたところ、Raspberry Pi 3B系の 64bitモードに対応している OSの一覧まとめ 2018年版というサイトを見つけた。

これによると先ほどのサイトに書かれていなかったUbuntu 18.04も動かせるらしい。これなら32bit版でも64bit版でもdocker-ceをインストールできそうである。MicroSDカードの作り直しから実施して今度こそKubernetesのインストールを成功させる。

MicroSDをフォーマットする

OSを入れ直すためにラズパイから取り出したMicroSDMacにつないで初期化する。

以下のコマンドで初期化

$ diskutil eraseDisk FAT32 RPI /dev/disk2 
Started erase on disk2
Unmounting disk
Creating the partition map
Waiting for partitions to activate
Formatting disk2s2 as MS-DOS (FAT32) with name RPI
512 bytes per physical sector
/dev/rdisk2s2: 60996480 sectors in 1906140 FAT32 clusters (16384 bytes/cluster)
bps=512 spc=32 res=32 nft=2 mid=0xf8 spt=32 hds=255 hid=411648 drv=0x80 bsec=61026304 bspf=14892 rdcl=2 infs=1 bkbs=6
Mounting disk
Finished erase on disk2

初期化前と初期化後で比較してみる

# 初期化前
$ diskutil list
/dev/disk0 (internal):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                         500.3 GB   disk0
   1:                        EFI EFI                     314.6 MB   disk0s1
   2:          Apple_CoreStorage Macintosh HD            499.3 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3

/dev/disk1 (internal, virtual):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                  Apple_HFS Macintosh HD           +499.0 GB   disk1
                                 Logical Volume on disk0s2
                                 EF1D93DE-2460-4A71-B9DD-9C2223020689
                                 Unlocked Encrypted

/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *31.5 GB    disk2
   1:                 DOS_FAT_32 NO NAME                 700.4 MB   disk2s1
   2:                 Linux_Swap                         511.7 MB   disk2s2
   3:                      Linux                         30.2 GB    disk2s3

#初期化後
$ diskutil list
/dev/disk0 (internal):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                         500.3 GB   disk0
   1:                        EFI EFI                     314.6 MB   disk0s1
   2:          Apple_CoreStorage Macintosh HD            499.3 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3

/dev/disk1 (internal, virtual):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                  Apple_HFS Macintosh HD           +499.0 GB   disk1
                                 Logical Volume on disk0s2
                                 EF1D93DE-2460-4A71-B9DD-9C2223020689
                                 Unlocked Encrypted

/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *31.5 GB    disk2
   1:                        EFI EFI                     209.7 MB   disk2s1
   2:       Microsoft Basic Data RPI                     31.2 GB    disk2s2

ネットワークトラブル

無線親機を実家のネットワークに接続しようとしたら、「接続がタイムアウトしました」と出る。

使っている無線親機はBUFFEROのWMR-433W。調べるとファームウェアをアップデートするといいとのこと。参考

最新版のv1.5.2にアップデートしたものの結果は変わらず。

最終的には同じ機種を使っていた記事を見つけることができた。この記事を書いている間は実家に帰省していたこともあり、その場しのぎができればそれでよかった。そのため、この記事に習って実家にいるうちは有線でスイッチングハブとネットを接続することで解決することにした。

参考にしたサイト

ラズパイでKubernetesクラスタを構築する(物理編)

ラズベリーパイを触ったことがないので、後で見返すメモとして記録する。

この記事はラズパイケースにラズパイを設置する物理編である。

OSのインストールおよびKubernetesのインストールはラズパイクラスタにKubernetesのインストール(失敗編)とラズパイクラスタにKubernetesのインストール(成功編)の二つの記事に別途まとめる予定。

もともとこの記事を分割する予定はなかったものの、OSの選定が理由で作業が詰んでしまい大きく手戻りをすることになった。

その失敗をなかったことにするのはもったいないので、問題がなかった物理的な配線とOSインストール以降の失敗編と成功編の3つの記事に分けることにした。

用意したもの

年末に帰省したときに作業したので、実家にあるものはそのまま流用した。

ノートPCは除外するとして、最初から揃えるとするとだいたい3万円くらい?

必要なもの 用途
Raspberry Pi 3 Model B+ × 3 これがなくちゃ始まらない
4段積層式のラズパイケース ラズパイを縦に積み重ねるため。リンクのものはラズパイ用のヒートシンクが4セット入っているのでお得
MicroSD 32GB×3 ラズパイのディスクとして使う
USBキーボード ラズパイを操作するため。実家にあるだろうと思ったらなかったのでAmazonで注文
USBマウス ラズパイを操作するため。自宅のを借用
USB Type-C ハブ SDカードをMacBookProに認識させるため。自宅にも実家にもなかったのでAmazonで注文
USB充電器 ラズパイの電源
スイッチングハブ クラスタのSW
LANケーブル(15cm×1本, 30cm×3本) ラズパイ同士を接続するため
ラズパイ用のディスプレイ 実家のディスプレイを借用
HDMIケーブル ラズパイをディスプレイに接続するため。実家のPS4のケーブルを拝借
Micro USBケーブル 30cm×3本 ラズパイとUSB充電器を接続するため
無線親機 無線LANにつなぐため
両面テープ USB充電器などをケースに固定するため

全体の作業概要

  • 物理的な配線(本記事の範囲)
  • ラズパイにOSをインストール(ラズパイクラスタにKubernetesのインストール(失敗編/成功編))
    • OSイメージを焼いたMicroSDカードを用意
    • MicroSDを読み込ませてラズパイを起動
    • パスワード変更
    • パッケージの更新
    • /パーティションの拡張
    • ホスト名の設定
    • /etc/hostsの設定
  • Kubernetesのインストール(ラズパイクラスタにKubernetesのインストール(成功編))
    • Dockerをインストール
    • Kubernetesのインストール

作り方

まずはケースを開封するとケース本体と工具箱が出てくる。工具箱にはドライバ、ピンセット、ヒートシンクなど必要なものが揃っている。このピンセットはヒートシンクの取り付けやナットを締めるのに便利だった。

f:id:kuromt:20181229132008j:plain
ラズパイとケース

f:id:kuromt:20181229132116j:plain
ラズパイケースの工具箱に入っていたもの

まず、工具箱に入っているヒートシンクをラズパイの表に二箇所、裏に一箇所つける。

f:id:kuromt:20181229132402j:plain
表側

f:id:kuromt:20181229132425j:plain
裏側

つぎに、ケースのアクリル板についている保護シートをはずす。保護シートとアクリル板の間には隙間がほとんどなく、剥がすのに苦労する。そんな時はアクリル板の四隅にあるネジ穴の内側から外側に向かって工具箱のドライバで強めに引っ掻いてやると、保護シートがめくれ上がって剥がしやすくなる。ここがドライバの唯一の出番だった。

f:id:kuromt:20181229132838j:plain
引っ掻いたおかげで剥がしやすくなった

保護シートを外したら説明書に従ってラズパイを取り付けていく。先ほど書いたように、ナットは付属のピンセットを使ったり爪で締めた。

f:id:kuromt:20181229133023j:plain
1段目

f:id:kuromt:20181229133041j:plain
3段目

4段目にはUSB充電器を設置する。両面テープを使って固定する。

f:id:kuromt:20181229133226j:plain
USB充電器

f:id:kuromt:20181229133727j:plain
USB充電器の向き

最後に天板を取り付ける。ラズベリーがおしゃれ。

f:id:kuromt:20181229133109j:plain
4段目

ただし、すぐに天板の上にスイッチハブと無線親機を乗せることになるのこのラズベリーとはお別れである。

f:id:kuromt:20181229133517j:plain
スイッチハブと無線親機

スイッチハブと無線親機を15cmのLANケーブルで接続する。

f:id:kuromt:20181229133609j:plain
LANを配線後

ラズパイ本体にもLANを挿してスイッチングハブと接続する。

f:id:kuromt:20181229134247j:plain
ラズパイのUSBポートは手前、最上段のスイッチハブのUSBポートは左についている

こうして全ての配線が済んだ。 本当はUSB充電器と同じようにスイッチングハブと無線親機も両面テープで固定したいが、年明けに実家から帰宅する途中に剥がれてしまいそうなので帰宅するまで我慢する。

f:id:kuromt:20181229134418j:plain
接続後の状態

あとはUSB充電器とスイッチングハブを電源に接続して完了。

物理的な配線を終えたので、本来であれば無線親機を設定する。

しかし、手元のWMR-433WがPPPoEに対応していなかったため実家の環境では無線の設定ができないことが分かったため、スイッチングハブを有線で接続した。 参考

参考にしたサイト

Golangでのつまずき その2

実践Go言語の説明を読んで実際にコードに書いて動作を確認したときにつまづいたポイントをまとめた。

埋め込みの動作確認でコンパイルエラー

また、埋込みを使うことで少し扱いやすくもなります。下の例では、通常の名前付きフィールドと並んで、埋込みフィールドを記述しています。

 type Job struct {
   Command string
    *log.Logger
 }

これでJob型は、*log.Logger型が持つLog、Logfといったメソッドを持つようになりました。もちろん、Loggerにフィールド名を与えることはできますが、それは必須ではありません。これで一度初期化すればJobのログが記録できるようになります。

  job.Log("starting now...")

この動作確認のため、以下のようなコードを書いた。

package main

import "log"

type Job struct {
    Command string
    *log.Logger
}

func main() {
    job := new(Job)
    job.Command = "aaa"
    job.Log("hoge")
}

コンパイル結果はjob.Log undefined (type *Job has no field or method Log)になった。

少なくとも、logの中でLog関数が定義されているらしき場所は見つからなかった。

説明の中で触れているlogが組み込みのlogではなかったのか、手元のGoのバージョンでlogの振る舞いが変わったのか。

Golangでのつまずき

Go言語を覚えるために試してひっかかったポイントをまとめた。

Goのバージョンはgo version go1.10.3 darwin/amd64

  • :=を使った暗黙の変数定義を使えるのは関数の中だけ
    • aの定義をmain関数の中でするか,aの宣言にvarを使えばいい
    • :=のスコープが関数の中にしかないのはGoの仕様
package main
package main
import "fmt"
      
a:=5
func main() {
  if a > 2 {
    fmt.Println("a > 2")
  } else {
    fmt.Println("a <= 2")
   }
}

//コンパイル結果: ./main.go:5:1: syntax error: non-declaration statement outside function body   
  • 同じパッケージにある別のファイルで定義した関数の呼び出しでエラー

    • VSCodeではapp1.goのHoge()に定義ジャンプできるが、app1.goをビルドするとHogeは未定義だと言われる
    • go run app1.goで実行していたが、go runは指定したファイルのみがコンパイル対象になり、自動的に他のファイルを見てくれるわけではないとのこと
    • go run app1.go app2.goと実行すれば正しく動作する
  • app2.go

package main

// Compare compares byte arrays.
func Compare(a, b []byte) int {
    for i := 0; i < len(a) && i < len(b); i++ {
        switch {
        case a[i] > b[i]:
            return 1
        case a[i] > b[i]:
            return -1
        }
    }
    switch {
    case len(a) < len(b):
        return -1
    case len(a) > len(b):
        return 1
    }
    return 0
}

// Hoge is snipet for Compare function
func Hoge() int {
    b1 := []byte{1, 2, 3}
    b2 := []byte{1, 2, 3}
    return Compare(b1, b2)
}
package main

import (
    "fmt"
)

func main() {
    a := Hoge()
    fmt.Print(a)
}

// 実行結果: ./app1.go:8:7: undefined: Hoge
マップのエントリを削除するには、並列代入をひっくり返して、追加の論理値を右に書きます。論理値がfalseならエントリは削除されます。キーが既にマップから削除済みであっても安全に行う事ができます。

timeZone["PDT"] = 0, false;  // Now on Standard Time
  • 書いたコード
package main

import (
    "fmt"
)

var timeZone = map[string]int{
    "UTC": 0 * 60 * 60,
    "EST": -5 * 60 * 60,
    "CST": -6 * 60 * 60,
    "MST": -7 * 60 * 60,
    "PST": -8 * 60 * 60,
}

var tz string
var seconds int
var ok bool

func main() {
    tz = "EST"
    timeZone[tz] = 10, false
    fmt.Println(timeZone[tz])
}

 // assignment mismatch: 1 variables but 2 values