CircleCIのmacOSビルドをOSSで利用する
kubelogin というKubernetes向けのツールをOSSで開発しているのですが、Goのビルド条件によって動作が異なる問題 *1 が出てきて、macOSでビルドを実行する必要が出てきました。ちょうど下記の記事でCircleCIがOSS向けにmacOSをサポートしていることを知りました。
CircleCIにメールで問い合わせたところ、すぐにOSS向けのmacOSビルドを有効にしてもらえました。先ほどの問題も無事に解決しました。ありがとうございます!
OSS向けのmacOSビルドを有効にするにはCircleCIのFreeプランが適用されている必要があります。私の場合はGitHub Marketplaceのプランになっていたため、GitHub Marketplace上でプランを解約したらCircleCIがFreeプランに戻りました。
CircleCIでmacOS executorを利用するのはとても簡単で、ジョブの設定を docker
から macos
に変更するだけです。今回はGoのビルドでDocker executorとmacOS executorを併用しているので、以下の点にハマりました。
- macOS executorでは自分でGoをダウンロードして配置する必要があります。Homebrewも利用できますが、ソースコードからビルドになるので時間がかかるので、ビルド済みパッケージを配置する方が時間が短くなると思います。
$GOPATH/pkg
をキャッシュしている場合はDockerとmacOSで別々にする必要があります。Docker executorで作成されたtar ballをmacOS executorで展開しようとするとエラーになります。(Go 1.14.4で確認)
$CIRCLE_BRANCH
などの環境変数はmacOS executorでも同じように使えます。
GitHub ActionsもmacOSビルドをサポートしており、こちらは問い合わせしなくても使えるのですが、今回はこれまでのビルドスクリプトをそのまま利用したかったためCircleCIを選択しました。
Certified Kubernetes Administratorを取得した
Certified Kubernetes Administrator(CKA)を取得しました。すでに多くの受験記があるので、他の人が書いていないことをまとめてみます。
きっかけ
いろんな言い訳でCKAの申し込みを先延ばしにしてきたのですが、以下の条件が揃ったので受験するに至りました。
- 会社で受験費用が支給される(同僚が事務処理をやってくれた)
- シェアオフィスが営業を再開したので個室を確保できる
- 勉強時間を捻出できた
- やる気が出た
事前準備
会社が契約しているシェアオフィスで個室を借りました。自宅からシェアオフィスまでは3.5km程度なので、自転車で行くことにしました。近くに駐輪場があるかどうかや個室が受験要件を満たすかどうかを確認しておきたかったので、前々日にリハーサルを行いました。まあシェアオフィスに着いたら普通に仕事していましたが。
試験は24時間前までに申し込む必要があります。試験の開始時刻はいろいろ選べます。私の場合は前々日に申し込みをしましたが、14時開始しか空いていませんでした。シェアオフィスの個室は、前後1時間の余裕を見て、13時から18時までを借りました。
前日までに以下を勉強しました。
- Kubernetes公式ドキュメントの構成や検索方法を把握する。
- Kubernetes The Hard Wayを実践する。
- kubeadmによるクラスタ構築や構成変更を実践する。
受験記でよく紹介されているUdemyなどのトレーニングコースは特にやりませんでした。まとまった時間を確保できる方はトレーニングコースを利用する方が効率的に学べるかもしれません。私の場合は断片的な時間で少しずつKubernetes The Hard Wayやkubeadmをやっていました。
参考までに、kubeadmによるSingle control-plane clusterやHighly available clusterの構築方法は下記にまとめています。
当日
当日は14時から開始でした。13時45分までに以下の準備を行いました。
- 机の上にある備品を一時的に片付ける。
- 外部ディスプレイを接続して動作を確認する。
- トイレに行く。
- ペットボトルのラベルを剥がしておく。
- 糖分を補給する。長女のラムネを拝借した。
13時45分になったら開始ボタンを押しました。監督員(Live Monitor)とはチャット上で英語でやり取りします。まずは環境要件を確認するのですが、私の場合は30分ぐらいかかりました。気を付けるとよさそうな点を以下に示します。
- スピーカーはOFFにしておく。ハウリングしてしまう。
- 個室が暗いのでカメラの映りが悪い。個室や机を明瞭に映すためにMacBookをゆっくり移動させる必要があった。
- 外部ディスプレイを利用する場合は、メインディスプレイに加えて外部ディスプレイも共有する。
- マスクは外す必要がある。
- 監督員からなかなか返信がこない。不安になるが辛抱強く待つ。
- チャットで長文の英語が送られてくるのでウッとなる。辛抱強く読む。(たぶんテンプレート化されている)
個室の机には備え付けの電灯があり、取り外せないので気になっていたのですが、特に指摘はありませんでした。
14時20分から試験を開始しました。試験時間は3時間なので、17時20分が終了時刻になります。以下、雑感です。
- 簡単な問題と難しい問題が混ざっている。難しい問題はフラグを立てて後回しにするとよい。
- 日本語の意図がよく分からない場合は英語の原文を読むとよい。
- 延々と問題が続くのでトイレ休憩のタイミングがつかめない。私の場合は1時間半ぐらい経過した時点でトイレに行き、個室に戻ってからストレッチと糖分補給を行った。
- Kubernetesの公式ドキュメントやGitHubリポジトリ以外のサイトにうっかりアクセスしないように注意する必要がある。
- シンプルなメモ帳ツールが用意されている。コピペが必要な場面で活用するとよい。
- カメラや画面の共有でCPUに負荷がかかるため、MacBook Airがファン全開で熱くなる。集中力が削がれてしまう。
同じ姿勢を3時間も続けていると体がおかしくなります。終わったらストレッチをしましょう。
事後
36時間以内に結果がメールで送付されます。私の場合は翌々日の0時にメールが来ていました。CNCF Trainingのポータルにアクセスすると成績を参照できます。
私の成績は以下でした。
- Your Score: 94%
- Score Needed to Pass: 74%
お約束の証書を貼っておきます。
ご参考まで。
Terraformでcloud-configを記述する
AWSのEC2インスタンスでは、User Dataにシェルスクリプトを渡すことで起動時にスクリプトを実行できます。ファイルを配置したり、パッケージをインストールしたり、といった複雑なことがやりたい場合はcloud-initが便利です。詳しくは公式ドキュメントの例を参照してください。
例えば、下記のようなYAMLをUser Dataに指定すると、OSの起動時にsystemdで独自のサービスを開始できます。
#cloud-config write_files: - path: /etc/systemd/system/helloworld.service" content: | [Unit] Description = Hello World [Service] ExecStart = /usr/local/bin/helloworld.sh Restart = always Type = simple [Install] WantedBy = multi-user.target - path: /usr/local/bin/helloworld.sh" permissions: 0755 content: | #!/bin/sh while true; do date; sleep 1; done runcmd: - ["systemctl", "start", "helloworld"] - ["systemctl", "enable", "helloworld"]
Terraformでcloud-configを指定する場合、下記のように外部ファイルを読み込むと簡単に書けます。
resource "aws_instance" "this" { user_data_base64 = filebase64('./cloud-config.yaml') }
ただ、YAMLの中にインラインでシェルスクリプトなどをベタ書きしているため、可読性がよくないというデメリットがあります。エディタで開いてもsyntax highlightingしてくれません。
そこで、下記のように動的にYAMLを組み立てることで、シェルスクリプトなどのファイルを外部化できます。
resource "aws_instance" "this" { user_data_base64 = base64encode(join("\n", [ "#cloud-config", yamlencode({ write_files : [ { path : "/etc/systemd/system/helloworld.service", content : file("./helloworld.service"), }, { path : "/usr/local/bin/helloworld", content : file("./helloworld.sh"), permissions : "0755", }, ], runcmd : [ ["systemctl", "enable", "helloworld"], ["systemctl", "start", "helloworld"], ], }) ])) }
サンプルが下記にあるので参考にどうぞ。