タイトル通りですが、1年目エンジニアのインフラのイの字も知らなかった私が1ヶ月半かけてKubernetesで環境構築するまでの失敗の軌跡です(そして環境構築できたのが奇跡)。
理想的にはこれを読めばインフラ初心者でもKubernetes(以下k8s)で環境構築できるところまで説明することですが、そういうわけでなく、環境構築の解説というより自分の失敗やつまづきポイント、役に立ったことをただただ書き連ねていきます。ただ他の初心者の方も同じようなところでつまづくこともあると思うので少しでもお役に立てたら嬉しいです。
バックエンド側で使った技術は以下になっています。
- 言語:Ruby(RoR)
- API:GraphQL
- インフラ:Azure
- その他:Docker、k8s
実際の実装でハマったところは各章の最後に教訓として簡単にまとめてはいますが、大事なことは先に結論として述べておきます。
- Dockerfileが環境構築の定義書になっているので、まずはDockerfileを理解する。
- クラウドサービスはドキュメント読み進めながら触って覚える。
- k8sで立ち行かなくなったらマシンのスペックやpodのリソースの容量を上げる。
- 途中で諦めない(諦められない)。
#目次
第一章:新サービス始動!バックエンドチームへ!
第二章:Dockerの洗礼
第三章:クラウドサービスの奥深さ
第四章:Kubernetesやってみっか(終わりの始まり)
最終章:俺たちの戦いはこれからだ!
まとめ
#第一章:新サービス始動!バックエンドチームへ!
弊社で新しいサービスを立ち上げるにあたってフロントエンドチームとバックエンドチームを編成することになりました。
バックエンドチームはインフラの構築も含まれており、バックエンドの方が好き、かつインフラも勉強してみたいと思った向上心が高め()の私は迷わずバックエンドチームに希望を出しました。
リモート会議で弊社CTOが「じゃあ、どんな感じでチー」くらいで食い気味にバックエンドチームがいいです!と言ったところすんなりと希望は通りました。そして意気揚々と新しい開発の幕開けに心を踊らせていたわけです。インフラの沼にハマるとは知らずに。
この時は言ってみるもんだなって感じでしたが、やはりやりたいことはどんどん主張した方が良いなって思いました。
ただあまり主張しすぎるのも周りから嫌われるかもしれないので程度が大事ですね(今回はフロントもバックエンドも良い感じで希望通りになったので結果的に言えば特に主張する必要なかったです)。
#第二章:Dockerの洗礼
Dockerの導入
今まで開発にDockerを使っていなかったため、Dockerを導入することになりました。
私も一回DockerでRailsの環境を作ったことはありますが、当時は完全にコピペで何をしてるのか全く分かっていなかったし、何が良いのかも分かっていませんでした。
もはやDockerはスタンダードな技術ですし、ローカルで環境作ったらそのままリモートにも簡単に持っていけると思っていたので勉強をはじめました。簡単に持って行けたら苦労しないっていう話ですね。まじで。
Dockerとは何か、みたいな話は他の文献や記事にお任せします。
webサーバーはnginx、appサーバーはRails、DBはpostgreSQLという割と王道な構成で作ることになりました。
とりあえず動けばいいや
最初の目標はとりあえずローカルでRailsが使えるようになるところまでです。
色々参考にしてとりあえず動くDockerfile、docker-composeを作成しました。
今でこそDockerfileって分かりやすすぎじゃない?って思いますが、まず最初の時点でイメージとコンテナの違いすらあまり分かっていなかったレベルだったので、何をしてるのかさっぱり分かっていませんでした。
FROMでイメージを取ってくる。(イメージとは?)
RUNでコマンド実行、ふむふむ。apt-get、はいはい(apt-getとは?)
みたいな感じです。
とりあえず動けばいいや理論で進めていったのでエラーが出ても対処の仕方が分かりませんでした。
インフラ初心者はDockerを理解するのにまずはサンプルのDockerfileを完全に理解することをオススメします。
これは仮にDockerを使わなくてもどうやって環境構築するかがDockerfileを読めば分かるからです。つまりDockerfile自体が環境構築の仕様書になっています。
ベースとなるイメージを取ってきたあとはVMに環境を構築する手順とほとんど変わりはありません。ここが分かってないとDockerを使おうが、使うまいが自分が構築した環境が何をしているか理解できないまま運用となり、エラーが出た時に対処するのに時間がかかるかもしれません。
結局本番と開発は違うdocker-compose
dockerの設定ファイルを一回作っちゃえばローカルでも本番でも同じ設定で環境構築できると思っていた時期が私にもありました。
Dockerfileはほぼほぼ同じファイルを使っていますが、docker-composeは結構違います。
先にも記述しましたがwebサーバーにはnginx使っていますし、DBにはPostgreSQLを使っています。
そのためdocker-composeはnginxやPostgreSQL、あとwebpack-dev-serverのコンテナの起動も含んでいました。
これでローカル動いてたので、本番環境もこれでいけるやんって思ってたのは大きな間違いでした。
まずnignxは別にローカルではなくてもいいです(もちろんあってもいい)し、rails6標準でwebpacker入っていますが、特に使っていなかったのでwebpack-dev-serverいらないですし、本番ではローカルファイルをマウントしません。あと、DBは外部サービス使うのでコンテナいらないです。
よく考えたらDocker使ってない時もローカルと本番では設定が違うので当たり前っちゃ当たり前ですね。
本番まで構築することを考えると本番とローカルで何が違うのかを理解するのは大事ですね。
Dockerだけでなくrailsの設定もローカルと本番で違うのでどのファイルをいじるかもちゃんと理解しないと意外な落とし穴がたくさんあります。特にconfigディレクトリはそんなんばっかです。ほんと気をつけましょう。
イメージ大きすぎひん?
とりあえず動けばいいやっていう考えで色々調べたもので設定ファイルを作っていたので、イメージのサイズがとても大きくなりました。
この時点ではイメージが大きくても特に問題はなかったのですが、色々ビルドをし直すと無駄な重いイメージが大量に出てくるのと、本番運用時にリポジトリにpushしたイメージが大きいとデプロイに時間がかかったりするので、なるべく小さいイメージにしたいと思っていました。(パフォーマンスも落ちるのかな?この辺は分からず、、、)
少なくともalpineのイメージ使いたいなぁと思っていたところこちらの記事を見つけたのでこちらを参考に小さいイメージを作りました。
ポイントはマルチステージビルドとdockerignoreです。(やり方は上記のリンクにて確認お願いします。)
これでイメージサイズは大体半分くらいになりました。
ちなみに使ってないイメージはdocker image prune -a
で一掃できます(消したくないものも混じっている可能性あるので自己責任で)。
因みにpruneは某中井貴一さんのフルーツと同じ綴りですが、ここでは切り取るとかそんな意味です。
エイリアスは幸せ
存在は知っていましたが何故か面倒くさがってエイリアスを登録せずに毎回docker-compose build
とか入力してました。docker-compose
を打ちすぎて多分docker-compose
のタイピングだけだったら全国レベルでした。
しかし今やdcu
でコンテナ立ち上がりますし、dcd
でコンテナ落とすことができます。幸せ。
エイリアスの登録は最初にやりましょう
その他
ローカルではビルドの高速化のためインストールしたgemをマウントしてるのですが、そのせいでgemfile.lockとの齟齬が起きたりしてたまにビルドに失敗することがありました。
そういう時は、docker volume prune
で一掃するとうまくいきます。
教訓
- 自分がどういう環境を作っているかを理解するために、まずはDockerfileを完全に理解すること。
- ローカルと本番ではdocker-composeは結構違う
- イメージサイズはできる限り小さくした方が良い
- いらないイメージは
docker image prune -a
- いらないvolumeも
docker volume prune
で一掃 - というか
docker system prune
で大体全部消える - エイリアスを登録するとコマンド入力が三倍速い
#第三章:クラウドサービスの奥深さ
本番環境はAzureを使うことになったので、まずはAzureに慣れることから始めました。
VM立てるのもお金がかかるわけでドキドキです。(本当に作っていいのかな、高額請求こないかな)
どのサービス使えばいいの?
まずDockerを使うということでコンテナサービスを調べました。
そこで出てくるのがContainer InstancesとWeb App for Containersです。
この違いは全く分かりませんでしたが、答えはこちらでした。
要はWeb App for Containersは通常のWebアプリを構築するのに使用し、Container Instancesはバッチ処理、例えば1日10分だけ使うスクリプト用のコンテナを建てるのに使用します。
あとはPostgreSQLを使うのでリソースの検索でPostgreSQLと検索するとたくさん出てきます。
よく見ると発行元がMicroSoftなのはAzure Database for PostgreSQLだけなのでこれを使えば良いのですが、初見だとよく分かりません。
用途は違うのに名前が似てるものは最初全く分かりませんでした。サードパーティ制はともかく似てる名前のサービスは公式で違いを教えてくれれば良いのにって思いましたね。初見殺し。
この辺は書籍を読んだり、知っている人に聞いたりしないと大分難しいですね。
特にAzureはクラウドサービスの中では公式以外の情報が少ないのでネットで調べても出てこないことが多いです(特に日本語では)。インフラ初心者すぎて調べ方が悪い説もありますが。。。
そういう意味ではAWSが一番情報が多い気がするので、選べるならAWSから勉強を始めた方が良いのかもしれませんが、Azure確定だったので、本読んだり公式ドキュメントを読むことで少しずつ何とか進めました。
とりあえずドキュメント、まずドキュメント、結局ドキュメントの精神ですね。
プレビュー版の罠
そんなこんなで、Web App for Containersでいけるかなって思い意気揚々と環境構築しようと思ったところ、思わぬところでつまずきました。
プレビュー??
つまり正式リリース前ってことですね。(最初何のこと?って感じでした)
ベータ版って名前の方が浸透してる気がするんですけどね?
いずれにせよ本番運用でプレビュー版を使うのはやめた方が良さそうです。
(MSの人に聞いたところ、プレビュー版から本番リリースで仕様がかなり変わることもあるのでやはり本番運用ではGAしてからの方が良いようです)
つまりWeb App for Containersを使いたかったら単一のコンテナのみしか受け付けないということです。nginx使えないじゃん。
今気づいたんですが、Web App for Containersって複数形なのに複数使えないとは何事であろうか。
ここでWeb App for Containersを使うのは諦めてVMにDockerをinstallして一から構築しようと決めましたが、今考えるとサービス初期段階ではnginxなしでrailsコンテナだけのWeb App for Containers使っても良かったかなって思います。
開発スケジュールにもよりますが、Web App for Containersを使っていれば環境構築は結構すぐに終了して他のことに時間を使うことができたのでそういう選択はアリだったかもしれません。
ただ、Web App for Containersのようなフルマネージドなサービスは細かい設定をよしなにやってくれる一方で、その細かい設定を変更することができない(もしくは難しい)ので、サービスの使用状況によっては結局は自分で一から環境構築した方が良いこともあると思います。
この辺のバランス感覚はインフラの経験を積まないと分からないでしょうし、どのサービス使うのかとかも使わないと分からないので、結局使って慣れるしかないんでしょうね。
まずはセキュリティ
そんなこんなでVMを建てることになりました。
SSHキーの概念くらいは知っていたので、とりあえずSSHキーでログインできるようにしました。
ポート番号くらい変えておいた方が良いということで、ポート番号を変えてみたところ繋がりません。
Azure側で設定が必要でした。
ネットワークセキュリティグループという概念(他のサービスだと他の名前がついてる)があって接続の種類や接続元に制限をかけられます。これでそのポート番号を許可しなければいけなかったんですが、知らないと知らないですよね、そんなこと。知らないと知らないだらけです。
VMにつなげるDBやストレージサービスもIPの許可を設定する必要があり、まずは接続できるサービスを制限することで外部から攻撃されないようにするんですね。インフラは安定稼働も大事ですが、セキュリティも同じくらい大事ということを学びました。
クラウドサービス側でできるセキュリティ設定はなるべくやっておくことが大事ですね。
セキュリティの話は調べるとすごい出てくるんですが、とても難しいのでこれも経験がものを言うのかなって気もしてます。
便利なサービスにも裏はある
とりあえず開発用サーバーの構築をすることになりました。
やってることはVMにDocker等をインストールしてコンテナを建てるだけなのですが、一応SSL化した方が良いかなぁということでSSL化に手を付けました。
AzureでどうやってSSL化するんだろうとか、そもそもSSL化ってどうやるんだろうとか色々調べてたらとんでもないサービス見つけました。
ドン!!
https-portal
すんごい便利です。
ほとんど設定することなく勝手にLet's encryptでhttpsにしてくれます。しかも証明書も自動で更新してくれる親切仕様。
Dockerを勉強しはじめておそらく一番感動したかもしれません。
これのおかげで開発用サーバーを楽々SSL化してしばらくは問題なく使っていました。
しかしフロント側から画像がアップロードできないという問い合わせが。
何でだろう。Postmanでは上がるのに。。。
あれ?アップロードできる画像とそうじゃないのがあるぞ。
これは、nginxの設定だ!
そうです、https-portalはベースはnginxなのでnginxの設定が必要だったのです。
そもそもnginxの設定ファイル見ても、???だった私は初期設定で使っていました。
https-portalのリポジトリをちゃんと読むとnginxの設定ファイルをオーバーライドする方法もちゃんと載っており、これに従ってCLIENT_MAX_BODY_SIZEを設定することにより無事画像をアップロードできるようになりました。
便利なサービスといえど自分で設定できる箇所はデフォルトで良いのか確認することが大事でした。
教訓
- 似たような名前のサービスがあるので気を付ける
- プレビュー版って意外に多い
- セキュリティの設定は大事
- 便利だからといってサービス任せにしない
#第四章:Kubernetesやってみっか(終わりの始まり)
環境構築も概ね終わったのですが、k8sに手を出すことになりました。
理由としては、
- リリースまでに少し時間がある
- CI/CDの設定とかがVMにたってるコンテナでやるより楽そう
- オートスケーリング設定しやすい
- 名前が格好いい
という感じで導入できそうなら導入することになりました。
(間に合わなくても開発環境と同じような構成で本番も作れば良いので)
k8sもスタンダードな技術になってきているので勉強したかったこともあるし、なんかKubernetesできますって言えたらカッケェみたいな感じ始めました。始める理由はなんでもいいんです。大事なのはやりきることです。
###とりあえず概念を学ぶ
新しいものを勉強する時に割と全体像から学ぶタイプなので、書籍やらネットの情報を読んでいました。
最初はとりあえず色々なコンテナをよしなにやってくれるんでしょ?的なノリで読んでたんですが、リソースの種類が多すぎて訳わからなくなってました。
Dockerの時はコンテナしかなかったのに、Replica Set? Ingressって何?L7LB?状態です。
これに関してはもう作りながら覚えていくしかないですね。
書籍等で勉強もできるかと思いますが、こちらの記事はとても良い記事でした。
数時間で完全理解!わりとゴツいKubernetesハンズオン!!
色々なリソースの種類がありますが、完全に異なるものではなく1個ずつレベルアップしていくイメージで使うことができるので1回何かを構築すれば大体何が何をするものなのか分かってくると思います。
###便利なサービスにも裏はある(その2)
概念を学んでもそう簡単に設定ファイルの書き方は覚えられません。
よく分かんないからdocker-composeから勝手に作ってくれないかなぁと思っていたらそんなサービスありました。
マジかよ。これで余裕じゃんと思ってたらPodが動きません。
まあそんな簡単にできたらみんなこれ使ってますよね。
便利なサービスに頼りっきりにしないというのをDockerの時に学んだというのに全く学習してません。
とはいえ概ねの構成を生成できたので定義ファイルが何してるかをちゃんと勉強しながら修正して少しずつ形にしていけました。
便利なサービスは分かってる人が使うから便利なんですね。
###動かないPod
色々構成を変えたり、Railsの実装を変えるとPodが動かないことが何度かありました。
理由は様々ですが、イメージの名前やバージョンを間違っていたこともありましたし、ボリュームマウントがちゃんと指定できていなくて、必要な情報がうまく渡っていなかった(Railsのマスターキーとか)こともあります。
あとは、ImagePullPolicy: IfNotPresent
にしていたためだと思いますが、古いイメージと新しいイメージが混在していることもありました。ImagePullPolicy: Always
にすると毎回ちゃんとイメージを取ってきてくれるので問題なくなりました。
理由はともあれ、まずはデバッグの方法を知っておくことが大事だと思います。
デバッグできればたいていのことは結構すぐ解決します。
k8sだとkubectl describe pod [pod id]
でpodの詳細見れますし、kubectl logs [pod id]
でコンテナのログも出ます(pod内に2つ以上コンテナがある場合は、-c [container name]
でコンテナを指定できます)。
podは動くけどつながらない時は他のpodからcurlコマンドでリクエスト返ってくるか確認したり、色々な方法があるので頭に入れておくと良いかと思います。
また、k8sは色々なリソースを1個の定義ファイルに書けるのですが、リソースごとに切り分けた方がデバッグもしやすくなるので、最初のうちは定義ファイルは全部別で書いた方が良いなって思いました。慣れてきたら一緒にしても良いのかもしれません。
###いらなかったnginx
最初はよくわからず、nginxとRailsを同じpodに構成して1pod2containersで構成していました。
しかし色々進めていくと、AzureでSSL化するのにnginx-ingressとcert-managerを使うことになり、あれ?nginxいらないじゃんってなりました。
最初からそこを把握しておけば自分でnginxの設定ファイル書いたりrailsとの連携する必要なかったので、実装の前に構成を考えるべきだったなぁと思います。まあ本当に初心者だったので、やりながら構成していくしかなかったんですが。。。
因みに某有名draw.ioがVS Codeで使えるようになっており、しかもAzureのリソースの画像が入ってるので爆速で構成図を書くことができます!是非使ってください!
ただし、Azureの項目だけではなく、Cloud&Enterpriseの中にAzureのリソースが入ってたりするのでご注意を!
話は戻りますが、nginxを使う=nginxの設定が必要ということで、https-portalの時とは方法は異なりますがnginxの設定のオーバーライドも必要になります。ここはhttps-portalの時の失敗が役に立って、絶対どこかに設定あるだろって調べました。やはり失敗は大事。
###画像がアップロードできない
ある程度環境構築が進み、ステージングで運用していると画像がアップロードできない問題が発生しました。
実装を調べたり、色々調べた結果リソースの問題ということが判明しました。
最適なリソースが分からなかったため、最初にpodに割りてるメモリを500Miとかにしていました。
それを2000Miとかに上げてみたところ画像のアップロードができました。
よくわかりませんが、k8sで起きる問題はメモリやCPUをあげると解決するとどこかの記事で読んだことがあります。まさか自分の身に起こると思ってませんでしたが、本当でした。今後は困ったらリソースの上限をあげる方向で行きます。
###教訓
- 便利なサービスに頼りっきりにならない
- imagePullPolicyは常にAlwaysが良い
- デバッグの方法をちゃんと調べる
- リソースごとにファイルを分けた方が良い
- サービスの全容を把握してから設計する
- draw.ioめっちゃ便利
- nginx-ingressの設定
- リソースの調整も大事
#最終章:俺たちの戦いはこれからだ!
は?Kubernetes使えねぇ。
Kubernetesまじ神
を繰り返しながらなんとかk8s導入できました。
思っていたよりは簡単でした()
Azureのドキュメントを読めば設定色々できますし、ダウンタイムのことをあまり気にせずデプロイできます。
とはいえまだできてないことはたくさんあります。
CI/CDの設定簡単と聞いていたのにうまくデプロイできないし、podやnodeのモニタリング設定や、ロギングの設定もちゃんとはできていないのでこれから設定したいです。
他にもhelmの使い方を覚えて良い感じの設定ファイルも作りたい、せっかくk8s使うならマイクロサービス化したいし、やりたいことやらなくてはならないことはたくさんあります。
また、導入は簡単と言ったものの完全に理解してるわけではないので、何か問題が起きた時にインフラの問題なのか、コードの問題なのか分からない時もあります。(実際ありました)
本文中にも書きましたが、経験が大事な部分もあるので、良いのか悪いのかはさておき色々な失敗、トラブルを経験して堅牢な環境を構築できればと思います!
(調子に乗ってCKAでも取ってやろうかしら)
#まとめ
時系列順とは言いながらも実際は行ったり来たりしてましたし、実際はここに書いてないよく分からないエラーとかと戦いまくってなんとか環境構築までできました。
開発体勢的な話をすると、元々バックエンドチームは3人で一緒に開発を進めていたのにインフラやってるの私だけになりました。
最初:フロントエンドチーム、バックエンドチーム(私はここに所属)
最後:フロントエンドチーム、バックエンドチーム、私(インフラ)
になりました。解せぬ。
Docker構築はみんなで仲良くやってました。
途中までは私もインフラやりつつ、バックエンドのロジック実装もしてました。
それがいつしかインフラ触ってるの私だけ。
環境を作るのも私、みんなの公開鍵をサーバーにおくのも私、つまりインフラにおける全ての決定権も私。
幸か不幸かそれでインフラの勉強にはなりましたし、やはり手を動かさないといけませんね。インフラは途中で諦めるとかできないので最後までやりきることが大事ですね。
もちろん他の人に色々質問しまくりましたし、助けてもらいました。感謝感激雨霰です。
ほとんどポエムなこの記事を最後まで読んでくださり、ありがとうございました。
#参考文献
最後に参考にした記事や文献を最後にまとめておきます。本当はもっとたくさんあるのですが、どれを読んだか覚えてないので記事に関しては本文で触れたものだけにしておきます。
書籍
新しいLinuxの教科書
Microsoft Azure実践ガイド
3分間ネットワーク基礎講座
プログラマのためのDocker教科書
しくみがわかるKubernetes Azureで動かしながら学ぶコンセプトと実践知識
Kubernetes完全ガイド
###記事
RailsのDockerイメージを一番小さくする方法
What is the difference between Azure Container Instances and Web App for Containers?
数時間で完全理解!わりとゴツいKubernetesハンズオン!!
Azureドキュメント