3.5幎、50䞇行の囲.。 パヌト1

これは、ネむトフィンチの蚘事の翻蚳です- オリゞナル 2017幎3月24日公開


Go- Jjujuで曞かれた最倧芏暡のオヌプン゜ヌスプロゞェクトの3.5幎の䜜業の埌、2017幎1月31日はCanonicalでの最埌の日でした。


執筆時点では、メむンのJujuリポゞトリは3,542ファむル、540,000行のGoコヌドですこの数には65,000行のコメントは含たれおいたせん。 暙準ラむブラリを陀くすべおの䟝存関係を考慮するず、Jujuには9,523個のファむルが含たれ、その䞭には1,963,000行のGoコヌド331,000行のコメントを陀くが含たれおいたす。


このプロゞェクトの玄7,000時間の䜜業から孊んだ教蚓を次に瀺したす。


Jujuチヌムの党員が私に同意しおいるわけではなく、コヌドベヌスが非垞に倧きいため、1幎間䜜業しおコヌドの2/3を芋るこずができないこずに泚意しおください。 そのため、次のこずを懐疑的に考えおください。


ゞュゞュ


Jujuは、NomadやKubernetesなどに䌌たサヌビスオヌケストレヌションツヌルです。 Jujuは䞻に2぀のバむナリファむルで構成されたすクラむアントずサヌバヌ。 サヌバヌはいく぀かの異なるモヌドで動䜜できたすサヌバヌがいく぀かの異なるファむルの圢匏になる前は、99同䞀であったため、配垃しやすいファむルを䜜成する方が簡単でした。 サヌバヌは、遞択したクラりドで実行できたす。 個別のマシンで耇数の远加むンスタンスを実行でき、それらは䞭倮サヌバヌによっお管理されたす。 クラむアントおよび補助マシンは、RPC over Web゜ケットを介しおメむンサヌバヌず通信したす。


ゞュゞュは䞀枚岩です。 マむクロサヌビスはありたせん。1぀のバむナリファむルが必芁です。 Goは競争力に優れおいるため、ゎロチンが䜕かをブロックするこずを心配する必芁はありたせん。 したがっお、すべおを1぀のプロセスで䜿甚するず䟿利です。 シリアル化や他のプロセス間盞互䜜甚によるオヌバヌヘッドを回避できたす。 これにより、コヌドの盞互䟝存性が高たりたすが、開発においお責任の分離が垞に最優先されるわけではありたせん。 結局、モノリスは小さなサヌビスの束である堎合よりも開発ずテストがはるかに簡単であり、正しいコヌド分割ずカプセル化はコヌドの混乱を避けるのに圹立ちたした。


パッケヌゞ管理


Jujuは自動販売機を䜿甚したせん。 私の意芋では、それは䟡倀があるだろうが、プロゞェクトは通垞のツヌルが登堎する前に開始され、自動販売の䜿甚ぞの移行はそれに費やす時間の䟡倀がなかったようだ。 珟時点では、ロゞャヌ・ペッペのゎッドセップ ちなみにこれはgodepはありたせgodep を䜿甚しお改蚂版をキャプチャしおいたす。 確かに、圌には問題がありたす-圌はあなたのGOPATHの他のパッケヌゞを混乱させ、指定されたコミットに埓っおそれらを蚭定したす。そのため、自動販売機を䜿甚しない別のプロゞェクトをビルドするず、マスタヌからではなく䟝存関係がビルドされる可胜性がありたす-光。 ただし、リビゞョンをコミットするず、反埩可胜なビルドが埗られ倖郚リポゞトリで実際に誰もひどいこずをしない限り、問題はありたせんでした。 ただし 、コミットのハッシュを含むファむルは垞に競合ポむントでした。合䜵。 倚くの堎合、倚くの開発者によっおも倉曎されるため、遅かれ早かれ、2人がこのファむルの同じ行たたは隣接する行を倉曎したずきに状況が発生するはずです泚 godepsナヌティリティは、゜ヌスを曎新するファむルを䜿甚したすパッケヌゞずむンストヌルされるコミットのハッシュが瀺されたす。 これは本圓の問題になったので、これらの競合を自動的に解決するナヌティリティの䜜成を開始したした godepsはコミットされたコミットの日付を保存するため、ほずんどの堎合、最近のコミットを遞択するだけです。 glideを䜿甚する堎合、および単䞀ファむルに䟝存関係ハッシュを保存する同様のツヌルを䜿甚する堎合、問題は残りたす。 これを修正する方法がわかりたせん。


党䜓ずしお、私はパッケヌゞ管理が倧きな問題であるず感じたこずは䞀床もありたせんでした。 私たちの日垞の仕事では取るに足らないこずです。したがっお、パッケヌゞマネヌゞャヌが䞍足しおいるために人々がGoを真剣に受け止めないずいう話を読むのはい぀も奇劙でした。 ほずんどのサヌドパヌティのリポゞトリは安定したAPIをサポヌトしおおり、コヌドを特定のコミットにバむンドするこずもできたした...それは問題ではありたせんでした。


プロゞェクト組織


Jujuは80のモノリシックリポゞトリmonorepoで<github.com/juju/juju>にあり、残りの20のコヌドは別のリポゞトリ<github.com/juju>にありたす。 モノリポゞトリを割り圓おるこずには長所ず短所がありたす...コヌド党䜓で倧芏暡な倉曎を加えるのは簡単ですが、それはおそらくfoo/bar/baz/bat/alt/special安定したAPIを必芁ずしないこずを意味したす... そしお、これは、このモノリポゞトリからパッケヌゞをむンポヌトする人にずっおは党くの狂気に倉わり、パッケヌゞは今埌もほが同じ圢で存圚し続けるず考えおいたす。 もちろん、この堎合は自動販売機で節玄できたすが、アップグレヌドが必芁な堎合は幞運を祈りたす。


たた、モノリポゞトリは、API、責任の分担に぀いおの譊戒心が䜎く、コヌドが盞互䟝存しおいるこずを意味しおいたした。 私たちが䞍泚意だったず蚀うわけではありたせんが、APIの責任、品質、および安定性の区分が同じであるため、メむンのJujuリポゞトリ以倖のものははるかに暙準化されおいるように思われたす。 もちろん、倖郚リポゞトリのドキュメントも優れおいお、これ自䜓が倧きな意味を持ちたす。


倖郚リポゞトリの問題は、パッケヌゞの管理ずリポゞトリ間の倉曎の同期化でした。 倖郚リポゞトリを曎新した堎合、倖郚倉曎の䜿甚を開始するには、その埌メむンリポゞトリを倉曎する必芁がありたした。 もちろん、2぀のgithubリポゞトリに察しおこれをアトミックに行うこずは䞍可胜です。 たた、コヌド怜査やテストの倱敗などのためにメむンの倉曎を行うこずができない堎合があり、倖郚リポゞトリに互換性のない倉曎があり、この倖郚リポゞトリに倉曎を加えるこずを決定した人は぀たずきたす。


もう1぀蚀いたす。ナヌティリティリポゞトリは悪です。 倚くの堎合、以前のバヌゞョンのJujuのutilsリポゞトリのサブパッケヌゞに修正をバックポヌトしたしたが、他の倚くの無関係な倉曎がこの修正に远い぀くこずに再び気付きたした。 それは、1぀のリポゞトリにあたりにも倚くあるからです。 あらゆる皮類のひどいブランチ、「クランチ」、「コピヌアンドペヌスト」をしなければならなかったこずが刀明したしたが、䞀般的にこれはすべお悪いこずであり、そうしたせん。 utilsパッケヌゞずリポゞトリにnoずだけ蚀っおutils 。


普遍的なシンプルさ


Goのシンプルさは、間違いなくJujuプロゞェクトの成功の倧きな芁因でした。 以前に採甚した開発者の玄3分の1だけがGoで働いおいたした。 残りは新参者でした。 1週間埌、ほずんどの新芏参入者はすでに非垞に経隓を積んでいたす。 補品のサむズず耇雑さは、開発者にずっお蚀語自䜓よりもはるかに倧きな問題でした。 より経隓豊富なGo開発者が、GoでXを䜜成する最善の方法に぀いおチヌムから質問を受けたこずがありたしたが、それは非垞にたれでした。 これを以前の䜜品のCず比范しおください。以前の䜜品では、蚀語のさたざたな郚分を垞に説明しおいたした。


Goプログラミングの経隓がある開発者だけでなく、䞀般的に優秀な開発者を雇うこずができたため、シンプルさはプロゞェクトにずっお倧きな恩恵でした。 ぀たり、この蚀語がコヌドの新しい郚分を理解する䞊で障害になるこずはありたせんでした。 Jujuは非垞に巚倧だったため、プロゞェクト党䜓の詳现を誰も知るこずができたせんでした。 しかし、同時に、ほずんどすべおの人がコヌドにアクセスし、゚ラヌを含む100行皋床の行ず、それがどのように倚かれ少なかれ実行されるかを知るこずができたした。 新しいコヌドを孊習する際の問題のほずんどは、どの蚀語でもそうであるように、アヌキテクチャずは䜕か、情報はどのように䌝達されるか、期埅は䜕か元期埅です。


Goには魔法がほずんどないので、Goでこのプロゞェクトを実装するのは、他の蚀語よりも簡単だったように思えたす。 他の蚀語にある魔法はありたせん。 䞀芋シンプルで理解しやすいコヌド行に予期しない機胜を䞎えるこずができる魔法。 叀いGoコヌドを孊習するずき、「Goがどのように機胜するのか」ず疑問に思う必芁はありたせん。Goコヌドの䞀郚にすぎないからです。 もちろん、これは、掗脳される耇雑なコヌド、隠された期埅、前提条件がないこずを意味するものではありたせん...しかし、少なくずもこれは、基本的なアルゎリズムを曖昧にする蚀語機胜の背埌に隠されおいたせん。


テスト䞭


テストキット


Jujuでは、 gocheck Gustavo Nieyemerを䜿甚しおテストを実行したした。 gocheckの機胜のおかげで、フルスタックテストを実行し、各テストの前にJujuサヌバヌ環境ずmongoデヌタベヌスを自動的に展開するこずができ、開発者のオヌバヌヘッドが削枛されたす。 テストが䜜成された埌、それらは非垞に倧きいこずが刀明したしたが、この「ベヌスセット」をテストセットの構造に埋め蟌むだけで、すべおの汚れた䜜業が自動的に行われたす。 その結果、各テストで倚くのアクションが実行されたため、ナニットテストは非垞に生産的なラップトップでほが20分間実行されたした。 このような倧量のテストコヌドにより、テストコヌドは脆匱になり、理解ずデバッグが困難になりたした。 テストが成功たたは倱敗した理由を理解するには、テスト関数のオヌプン䞭括匧の前に実行されたすべおのコヌドを理解する必芁がありたした。セットにセットを埋め蟌むのは簡単なので、倚くの堎合、このオヌプン䞭括匧の前にLOTが行われたした。


将来的には、代わりに、テストのために暙準ラむブラリに固執したす。 暙準ラむブラリを䜿甚したテストは、通垞のGoコヌドず同じ方法で蚘述され、䟝存関係は明瀺的でなければなりたせん。 テストの開始時にコヌドを実行する堎合は、メ゜ッドをそこに配眮するだけで枈みたす...メ゜ッドをそこに配眮する必芁がありたす。


瓶の䞭のtime


timeパッケヌゞは、テストずテストコヌドの呪いです。 30秒埌にタむムアりトするコヌドがある堎合、どのようにテストしたすか 完了するのに30秒かかるテストがありたすか そしお、䜕かがうたくいかない堎合、残りのテストは30秒間実行されたすか これはtime.Sleepだけでtime.Sleep 、 time.Afterたたはtime.Tickerたす...䞀般に、これはテスト䞭の灜害です。 たた、テスト時特に-raceスむッチで開始するずきのコヌドは、実皌働時よりもはるかに遅く実行される可胜性があるこずは蚀うたでもありたせん。


解決策は時間をモックするこずです...これはもちろん、些现なこずではありたせん。なぜなら、 timeパッケヌゞは単なるトップレベル関数の束にすぎないからです。 したがっお、 timeパッケヌゞを䜿甚する堎合はい぀でも、代わりにtimeラッパヌである特別なむンタヌフェむスを取埗し、この停のtimeをテスト甚に枡す必芁がありたす。これは既に制埡できたす。 これにより、既補のアセンブリを䜜成し、コヌドの倉曎を配垃する時間が倧幅に増加したした。 長い間、それは垞に安定したテストの゜ヌスでした。 これらはほずんどの堎合に行われるテストですが、ある日CIマシンが思慮深いものであった堎合、いく぀かのランダムテストは倱敗したした。 たた、数十䞇行のテストがある堎合、䞀郚のテストが倱敗する可胜性が高く、ほずんどの堎合、前回ず同じテストではありたせん。 フレむキヌテストの修埩は、「ほくろを殺す」ゲヌムに䌌おいたした元モグラを叩きたす。9぀の穎からほくろが突き出るスロットマシンで、ハンマヌで叩かなければなりたせん 。


クロスコンパむル幞犏


OSずアヌキテクチャのすべおの組み合わせの正確な数はわかりたせんが、Jujuサヌバヌは、WindowsずLinuxCentosずUbuntuだけでなく、amd64だけでなく、ppc64le、arm64、s390xなどの゚キセントリックなアヌキテクチャも含め、倚くのアヌキテクチャ向けに間違いなく構築されおいたす。


Jujuは、gcコンパむラがサポヌトしおいないアヌキテクチャに最初にgccgoを䜿甚したした。 このため、gjuがずらえどころのないナンセンスを出したJujuでいく぀かのミスがありたした。 gcが曎新され、すべおのアヌキテクチャをサポヌトし始めたずき、远加のコンパむラをプロゞェクトから陀倖しおgcでのみ動䜜するこずを非垞に嬉しく思いたした。


gcに切り替えたずき、アヌキテクチャ固有の゚ラヌはほずんどなくなりたした。 サポヌトされおいるJujuアヌキテクチャの幅が広く、これらの゚キセントリックアヌキテクチャがCanonicalに倚くの圱響力を持぀倧䌁業で䜿甚されおいるずいう事実を考えるず、これは玠晎らしいこずです。


OS固有の゚ラヌ


最初に、Windowsサポヌトの組み蟌みを開始するず、OS関連の゚ラヌがいく぀か発生したしたすべおUbuntuで開発されたため、CIが機胜するたでWindows固有の゚ラヌは発生したせんでした。 基本的には、2぀の䞀般的なファむルシステム゚ラヌに芁玄されたす。


1぀目は、デフォルトでテストのパスに盎接スラッシュを䜿甚するこずです。 たずえば、構成ファむルがサブフォルダヌ "juju"にあり、 "config.yml"ず呌ばれるこずがわかっおいる堎合、テストでは、ファむルぞのパスがfolder + "/juju/config.yml"であり、Windowsであるこずが確認できたすfolder + "\juju\config.yml"でなければなりたせん。


テストでも新しいディレクトリを䜜成するずきは、特に文字列ずスラッシュを組み合わせおではなく、 path.Join代わりにfilepath.Joinを䜿甚したす。 filepath.Joinは、OSの正しいスラッシュを䜿甚したす。 パスを比范するには、垞にpath.ToSlashを䜿甚しお、既に比范可胜な暙準ビュヌにパスをキャストしたす。


もう1぀のよくある間違いは、Linux開発者が開いおいるファむルを削陀たたは移動できるようにするこずです。 たた、Windowsはファむルが開かれるずファむルをロックするため、Windowsでは機胜したせん。 これは、 defer file.Delete()呌び出しの圢で頻繁に発生し、FIFOによれば、 defer file.Delete()の遅延呌び出しの前に呌び出されたため、ただ開いおいるファむルを削陀しようずしたした。 恥ずかしい。 解決策の1぀は、移動たたは削陀を実行する前に、垞にfile.Close()呌び出すだけです。 Closeを数回呌び出すこずができるので、関数の最埌で動䜜defer file.Close()を既にdefer file.Close()おいる堎合でも、削陀する前に呌び出すのは非垞に安党です。


これらの゚ラヌはどれも難しくなく、暙準ラむブラリでのこのような匷力なクロスプラットフォヌムサポヌトにより、クロスプラットフォヌムコヌドの開発が簡単になるず思いたす。


゚ラヌ凊理


Goの゚ラヌ凊理は、間違いなくJujuの安定性に有益な効果をもたらしたした。 特定の関数が倱敗する可胜性のある堎所を知るこずができるずいう事実は、クラッシュを予期するコヌドを曞くのがはるかに簡単であり、それが優雅にそれを行いたす。


時間の経過ずずもに、Jujuは単に暙準errorsパッケヌゞを䜿甚したした。 ただし、゚ラヌの原因ずなったコヌドのパスを远跡するために、より倚くのコンテキストが必芁であるず感じ、゚ラヌに関するより詳现な情報を保存し、それにコンテキストを远加するこずも良いず考えたしたたずえば、 fmt.Errorfを䜿甚するfmt.Errorf情報は倱われたす元の゚ラヌに぀いお、たずえば、 os.NotFound゚ラヌの堎合。


数幎前、オリゞナルの゚ラヌ情報を倱うこずなく、より倚くのコンテキストをキャプチャする独自の゚ラヌパッケヌゞの開発を開始したした。 さたざたな方向に無益に投げた埌、すべおのアむデアをhttps://github.com/juju/errorsに結合したした。 もちろん、これは理想的なラむブラリではなく、長幎にわたっお新機胜のために拡匵されおきたしたが、良いスタヌトでした。


䞻な問題は、スタックトレヌスなどを出力する必芁があるずきに、珟圚のファむル名ず行番号を芋぀けるために゚ラヌを返すずきに垞にerrors.Trace(err)を呌び出す必芁があるこずです。 今日は、Dave Cheneyの<github.com/pkg/errors>パッケヌゞを遞択したす。これは、゚ラヌが生成された時点でスタックトレヌスを取埗し、完党なトレヌスを回避したす。 正盎なずころ、゚ラヌのあるスタックトレヌスは本圓に䟿利だずは思いたせん。 実際には、予期しない゚ラヌにはfmt.Errorf("while doing foo: %v", err)から十分なコンテキストがあるため、倚くの堎合、スタックトレヌスが必芁です。 元の゚ラヌのプロパティを調査する機胜は䟿利な堎合もありたすが、ほずんどの堎合、思っおいるほど頻繁ではありたせん。 foobar.init()がfoobar.init()ようなものを返す堎合、これは本圓にあなたのコヌドに圹立ちたすか ほずんどの堎合、いいえ。


安定性


このような巚倧なプロゞェクトの堎合、Jujuは非垞に安定しおいたすこれは、゚ラヌが倚くないずいう意味ではありたせん...クラッシュするこずはほずんどないか、非垞にバグが倚いこずを意味したす。 蚀語に倧きく䟝存するず思いたす。 Canonicalの前に働いおいた䌚瀟には、100䞇行のCコヌドがあり、「null reference」䟋倖やその他の未凊理の䟋倖で非垞に頻繁にクラッシュしおいたした。 正盎なずころ、Jujuプロダクションコヌドのヌルポむンタヌが原因でパニックに遭遇したこずは䞀床もありたせん。新しいコヌドで䜕か愚かなこずを行ったずきは、開発プロセスでたたにしかありたせんでした。


Goマルチリタヌンパタヌンが゚ラヌを瀺すために䜿甚されるず確信しおいたす。 テンプレヌトfoo, err :=を䜿甚し、垞に゚ラヌをチェックしたす。これにより、実際にはヌルポむンタヌが発生する可胜性が非垞に䜎くなりたす。 返された倉数にアクセスする前に゚ラヌをチェックするこずはGoの基本原則であるため、この芏則の䟋倖を文曞化するこずが重芁です。 远加の゚ラヌ戻り倀は、コンパむラが未䜿甚の倉数をチェックするため、無芖したり忘れたりするこずはできたせん。 これにより、他の同様の蚀語ず比范しお、GoでのNULLポむンタヌの問題がかなり軜枛されたす。


ゞェネリック


このセクションは短いので...たあ、あなたは知っおいたす。 ゞュゞュでの䜜業䞭に1、2回だけ、私は個人的にゞェネリックが䞍足しおいるず感じたした。 たた、コヌドのレビュヌ䞭に、他の人の゜ヌスのゞェネリックを垌望するこずを芚えおいたせん。 Cでゞェネリック医薬品ず出䌚った認知知的耇雑さを「匷打」する必芁がなかったこずを嬉しく思いたした。 Goむンタヌフェむスは99のケヌスで十分であり、 interface{}意味するものではありたせん。 Jujuでinterface{}を䜿甚interface{}こずはほずんどありたせんでしたが、ほずんどの堎合、䜕らかのシリアル化が行われおいたためです。


続く


これはすでにかなり長い投皿なので、やめる時だず思いたす。 API、バヌゞョニング、デヌタベヌス、リファクタリング、ロギング、むディオム、コヌド怜査などに぀いお、もっず具䜓的なこずを話すこずができたす。



Source: https://habr.com/ru/post/J325326/


All Articles