Teori dan praktik standardisasi layanan Docker

Informasi tentang topik arsitektur aplikasi microservice, yang telah berhasil mengisi keunggulannya, cukup hari ini untuk memutuskan apakah itu sesuai dengan produk Anda atau tidak. Dan sama sekali bukan rahasia bahwa perusahaan yang telah memutuskan untuk memilih jalan ini harus menghadapi banyak tantangan teknik dan budaya. Salah satu sumber masalah adalah overhead yang berkembang biak di mana-mana, dan ini juga berlaku untuk rutin yang terkait dengan proses produksi.



Sumber Gambar:


Seperti yang bisa Anda tebak, Anti-plagiarisme adalah perusahaan seperti itu, di mana pemahaman secara bertahap datang bahwa kami sedang dalam perjalanan dengan layanan-layanan mikro. Tetapi sebelum mulai memakan kaktus, kami memutuskan untuk membersihkan dan memasaknya. Dan karena semua satu-satunya solusi yang benar dan benar untuk masing-masing adalah unik, daripada slide DevOps universal dengan panah indah, kami memutuskan untuk hanya berbagi pengalaman kami sendiri dan mengatakan bagaimana kami telah membahas sebagian besar dari jalur khusus kami untuk, saya harap, sukses.


Jika Anda membuat produk yang benar-benar unik, jauh dan luas yang terdiri dari pengetahuan, hampir tidak ada peluang untuk menghindari jalur khusus ini, karena ini dibentuk oleh banyak jalur pribadi: mulai dari budaya dan data historis yang telah dikembangkan di perusahaan, diakhiri dengan kekhususan sendiri dan tumpukan teknologi yang digunakan. .


Salah satu tugas untuk setiap perusahaan dan tim adalah menemukan keseimbangan optimal antara kebebasan dan aturan, dan layanan-layanan mikro membawa masalah ini ke tingkat yang baru. Ini mungkin tampak bertentangan dengan gagasan layanan mikro, yang menyiratkan kebebasan luas dalam pilihan teknologi, tetapi jika Anda tidak fokus langsung pada masalah arsitektur dan teknologi, tetapi melihat masalah produksi secara keseluruhan, maka risiko berada di suatu tempat dalam plot β€œTaman Kegembiraan Dunia” cukup nyata. .


Namun, dalam buku "Creating Microservices" Sam Newman menawarkan solusi untuk masalah ini, di mana secara harfiah dari halaman pertama ia berbicara tentang perlunya membatasi kreativitas tim dalam kerangka perjanjian teknis. Jadi salah satu kunci keberhasilan, terutama dalam konteks sumber daya tangan bebas yang terbatas, adalah standardisasi segala sesuatu yang hanya dapat dinegosiasikan dan tidak ada yang benar-benar ingin melakukannya sepanjang waktu. Dengan menyusun perjanjian, kami membuat aturan main yang jelas untuk semua peserta dalam produksi dan pengoperasian komponen sistem. Dan mengetahui aturan mainnya, Anda harus setuju, memainkannya harus lebih mudah dan lebih menyenangkan. Namun, mengikuti aturan-aturan ini sendiri dapat menjadi rutin dan menyebabkan ketidaknyamanan bagi para peserta, yang secara langsung mengarah pada semua jenis penyimpangan dari mereka dan, sebagai akibatnya, kegagalan seluruh gagasan. Dan jalan keluar yang paling jelas adalah dengan memasukkan semua perjanjian dalam kode, karena tidak ada satu peraturan pun yang dapat melakukan otomasi dan alat yang mudah digunakan, yang penggunaannya intuitif dan alami.


Bergerak ke arah ini, kami dapat mengotomatisasi semakin banyak, dan semakin kuat proses kami menjadi seperti konveyor ujung-ke-ujung untuk produksi perpustakaan dan layanan mikro (atau tidak demikian).


Penafian
Teks ini bukan upaya untuk menunjukkan "sebagaimana mestinya", tidak ada solusi universal, hanya deskripsi posisi kita di jalur evolusi dan arah yang dipilih. Semua hal di atas mungkin tidak cocok untuk semua orang, tetapi dalam kasus kami ini masuk akal karena:

- Pengembangan di perusahaan dalam 90% kasus dilakukan dalam C #;
- Tidak perlu memulai dari awal, bagian dari standar, pendekatan dan teknologi yang diterima - ini adalah hasil dari pengalaman atau hanya warisan sejarah;
- Repositori dengan proyek .NET, tidak seperti tim, lusinan (dan akan ada lebih banyak);
- Kami ingin menggunakan pipa CI yang sangat sederhana, menghindari vendor lock-in sebanyak mungkin;
- Untuk pengembang .NET biasa, kata-kata "container", "docker" dan "Linux" masih dapat menyebabkan serangan horor eksistensial, tetapi saya tidak ingin mematahkan siapa pun melalui lutut.

Sedikit latar belakang


Pada musim semi 2017, Microsoft memperkenalkan pratinjau .NET Core 2.0 kepada dunia, dan tahun ini para astrolog C # bergegas untuk mendeklarasikan Tahun Linux, jadi ...



Sumber Gambar:


Untuk beberapa waktu kami, tidak mempercayai sulap, mengumpulkan dan menguji semuanya pada Windows dan Linux, menerbitkan artefak dengan beberapa skrip SSH, mencoba mengonfigurasi pipa CI / CD lama dalam mode pisau Swiss. Tetapi setelah beberapa waktu mereka menyadari bahwa kami melakukan sesuatu yang salah. Selain itu, referensi ke layanan-layanan dan wadah mikro semakin sering terdengar. Jadi kami juga memutuskan untuk naik gelombang hype dan menjelajahi arah ini.


Sudah pada tahap refleksi tentang kemungkinan masa depan layanan mikro kami, sejumlah pertanyaan muncul, mengabaikan yang, kami mengambil risiko dalam masalah baru di masa depan yang akan kami buat untuk diri kami sendiri dengan imbalan yang sudah diselesaikan.


Pertama, ketika melihat sisi operasi dunia microservice teoretis tanpa aturan, kami takut dengan prospek kekacauan dengan semua konsekuensi yang terjadi, termasuk tidak hanya kualitas hasil yang tidak dapat diprediksi, tetapi juga konflik antara tim atau pengembang dan insinyur. Dan mencoba memberikan beberapa rekomendasi, tidak dapat memastikan kepatuhan dengan mereka, segera tampak seperti usaha kosong.


Kedua, tidak ada yang benar-benar tahu cara membuat wadah dengan benar dan menulis file docker, yang, bagaimanapun, sudah mulai ramai di repositori kami. Selain itu, banyak "membaca di suatu tempat" yang semuanya tidak begitu sederhana di sana. Jadi, seseorang harus menyelam lebih dalam dan mencari tahu, lalu kembali dengan praktik perakitan kontainer terbaik. Tetapi prospek mengambil peran sebagai buruh pelabuhan buruh pelabuhan penuh waktu, dibiarkan sendiri dengan tumpukan file buruh pelabuhan, untuk beberapa alasan tidak menginspirasi siapa pun di perusahaan. Selain itu, ternyata, menyelam sekali jelas tidak cukup, dan bahkan terlihat baik dan benar pada pandangan pertama, itu bisa berubah menjadi salah atau tidak terlalu bagus.


Dan ketiga, saya ingin memastikan bahwa gambar yang diperoleh dengan layanan tidak hanya benar dari sudut pandang praktik wadah, tetapi juga dapat diprediksi dalam perilaku mereka dan akan memiliki semua sifat dan atribut yang diperlukan untuk menyederhanakan kontrol wadah yang diluncurkan. Dengan kata lain, saya ingin mendapatkan gambar dengan aplikasi yang sama-sama dikonfigurasi dan menulis log, menyediakan antarmuka tunggal untuk mendapatkan metrik, memiliki satu set label yang konsisten, dan sejenisnya. Penting juga bahwa perakitan di komputer pengembang menghasilkan hasil yang sama dengan perakitan di sistem CI apa pun, termasuk lulus tes dan menghasilkan artefak.


Dengan demikian, lahir pemahaman bahwa beberapa proses akan diperlukan untuk mengelola dan memusatkan pengetahuan, praktik, dan standar baru, dan jalur dari komitmen pertama ke citra buruh pelabuhan yang sepenuhnya siap untuk infrastruktur produk harus disatukan dan seotomatis mungkin, tidak melampaui persyaratan awal dengan kata terus menerus.


CLI vs. GUI


Titik awal untuk komponen baru, baik itu layanan atau perpustakaan, adalah membuat repositori. Tahap ini dapat dibagi menjadi dua bagian: membuat dan mengkonfigurasi repositori di hosting sistem kontrol versi (kami memiliki Bitbucket) dan menginisialisasi dengan membuat struktur file. Untungnya, sejumlah persyaratan sudah ada untuk keduanya. Oleh karena itu, memformalkannya dalam kode adalah tugas yang logis.


Jadi, apa yang seharusnya menjadi gudang kami:


  • Terletak di salah satu proyek, di mana nama, hak akses, kebijakan untuk menerima permintaan tarik, dll.;
  • Berisi file dan direktori yang diperlukan, seperti:
    • file dengan konfigurasi dan informasi tentang repositori SolutionInfo.props (lebih lanjut tentang itu di bawah);
    • kode sumber proyek di direktori src ;
    • .gitignore , README.md , dll.;
  • Berisi submodula Git yang diperlukan;
  • Proyek harus berasal dari salah satu templat.

Karena Bitbucket REST API memberikan kontrol penuh atas konfigurasi repositori, sebuah utilitas khusus diciptakan untuk berinteraksi dengannya - generator repositori. Dalam mode tanya jawab, ia menerima dari pengguna semua data yang diperlukan dan membuat repositori yang sepenuhnya memenuhi semua persyaratan kami, yaitu:


  • Menentukan proyek di Bitbucket untuk dipilih;
  • Memvalidasi nama sesuai dengan perjanjian kami;
  • Membuat semua pengaturan yang diperlukan yang tidak dapat diwarisi dari proyek;
  • Memperbarui daftar templat khusus (kami menggunakan dotnet templating ) untuk proyek dan menyarankan untuk memilihnya;
  • Mengisi informasi minimum yang diperlukan tentang repositori dalam file konfigurasi dan dalam *.md dokumen;
  • Ini menghubungkan submodul dengan konfigurasi pipa CI / CD (dalam kasus kami adalah Bamboo Specs ) dan skrip perakitan.

Dengan kata lain, pengembang, memulai proyek baru, meluncurkan utilitas, mengisi beberapa bidang, memilih jenis proyek dan menerima, misalnya, "Halo dunia!" layanan yang sudah terhubung ke sistem CI, dari mana layanan itu bahkan dapat diterbitkan jika Anda membuat komit yang mengubah versi menjadi nol.


Langkah pertama telah diambil. Tidak ada pekerjaan manual dan kesalahan, mencari dokumentasi, registrasi dan SMS. Sekarang mari kita beralih ke apa yang dihasilkan di sana.


Struktur


Standarisasi struktur repositori telah berakar bersama kami untuk waktu yang lama dan diperlukan untuk menyederhanakan perakitan, integrasi dengan sistem CI dan lingkungan pengembangan. Awalnya, kami mulai dari gagasan bahwa pipa di CI harus sesederhana dan, seperti yang Anda duga, standar, yang akan memastikan portabilitas dan reproduktifitas perakitan. Artinya, hasil yang sama dapat dengan mudah diperoleh baik di sistem CI apa pun dan di tempat kerja pengembang. Oleh karena itu, segala sesuatu yang tidak berhubungan dengan fitur-fitur lingkungan integrasi berkesinambungan spesifik disampaikan ke submodule Git khusus dan merupakan sistem pembangunan mandiri. Lebih tepatnya, sistem standarisasi perakitan. Pipa itu sendiri, dengan perkiraan minimum, seharusnya hanya menjalankan skrip build.sh , mengambil laporan tentang tes dan memulai penyebaran, jika perlu. Untuk kejelasan, mari kita lihat apa yang terjadi jika Anda membuat repositori SampleService dalam proyek dengan nama Sandbox .


 . β”œβ”€β”€ [bamboo-specs] β”œβ”€β”€ [devops.build] β”‚ β”œβ”€β”€ build.sh β”‚ └── ... β”œβ”€β”€ [docs] β”œβ”€β”€ [.scripts] β”œβ”€β”€ [src] β”‚ β”œβ”€β”€ [CodeAnalysis] β”‚ β”œβ”€β”€ [Sandbox.SampleService] β”‚ β”œβ”€β”€ [Sandbox.SampleService.Bootstrap] β”‚ β”œβ”€β”€ [Sandbox.SampleService.Client] β”‚ β”œβ”€β”€ [Sandbox.SampleService.Tests] β”‚ β”œβ”€β”€ Directory.Build.props β”‚ β”œβ”€β”€ NLog.config β”‚ β”œβ”€β”€ NuGet.Config β”‚ └── Sandbox.SampleService.sln β”œβ”€β”€ .gitattributes β”œβ”€β”€ .gitignore β”œβ”€β”€ .gitmodules β”œβ”€β”€ CHANGELOG.md β”œβ”€β”€ README.md └── SolutionInfo.props 

Dua direktori pertama adalah Git submodules. bamboo-specs adalah β€œPipeline as Code” untuk sistem Atlassian Bamboo CI (mungkin ada beberapa Jenkinsfile sebagai gantinya), devops.build adalah sistem build kami, yang akan saya bahas secara lebih rinci di bawah ini. Direktori .scripts juga .scripts . Proyek .NET itu sendiri terletak di src : NuGet.Config berisi konfigurasi repositori NuGet pribadi, NLog.config konfigurasi dev-time NLog . Seperti yang Anda duga, menggunakan NLog di perusahaan juga merupakan salah satu standar. Yang menarik di sini adalah file Directory.Build.props hampir ajaib. Untuk beberapa alasan, beberapa orang tahu tentang kemungkinan seperti itu dalam proyek .NET, seperti kustomisasi perakitan . Singkatnya, file dengan nama Directory.Build.props dan Directory.Build.targets otomatis diimpor ke proyek Anda dan memungkinkan Anda untuk mengonfigurasi properti umum untuk semua proyek di satu tempat. Sebagai contoh, ini adalah bagaimana kami menghubungkan penganalisa StyleCop.Analyzers dan konfigurasinya dari direktori CodeAnalysis ke semua proyek bergaya kode, menetapkan aturan versi dan beberapa atribut umum untuk pustaka dan paket ( Perusahaan , Hak Cipta , dll.), Dan juga terhubung melalui <Import> file SolutionInfo.props , yang tepatnya merupakan file konfigurasi repositori yang sama, yang telah dibahas di atas. Ini sudah berisi versi saat ini, informasi tentang penulis, URL repositori dan deskripsinya, serta beberapa properti yang mempengaruhi perilaku sistem perakitan dan artefak yang dihasilkan.


Contoh `SolutionInfo.props`
 <?xml version="1.0"?> <Project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="devops.build/SolutionInfo.xsd"> <PropertyGroup> <!-- Product name --> <Product>Sandbox.SampleService</Product> <!-- Product version. Version 0.0.0 wouldn't be published! --> <BaseVersion>0.0.0</BaseVersion> <!-- Project name which contains Main() --> <EntryProject>Sandbox.SampleService.Bootstrap</EntryProject> <!-- Exposed port --> <ExposedPort>4000/tcp</ExposedPort> <!-- DOTNET_SYSTEM_GLOBALIZATION_INVARIANT value. See https://github.com/dotnet/corefx/blob/master/Documentation/architecture/globalization-invariant-mode.md --> <GlobalizationInvariant>false</GlobalizationInvariant> <!-- Project URL --> <RepositoryUrl>https://bitbucket.contoso.com/projects/SND/repos/sandbox.sampleservice/</RepositoryUrl> <!-- Documentation URL --> <DocumentationUrl>https://bitbucket.contoso.com/projects/SND/repos/sandbox.sampleservice/browse/README.md</DocumentationUrl> <!-- Your name here --> <Authors>User Name &lt;username@contoso.com&gt;</Authors> <!-- Project description --> <Description>The sample service for demo purposes.</Description> <!-- Bamboo plan key (required for Bamboo Specs --> <BambooBlanKey>SMPL</BambooBlanKey> </PropertyGroup> </Project> 

Contoh `Directory.Build.props`
 <Project> <Import Condition="Exists('..\SolutionInfo.props')" Project="..\SolutionInfo.props" /> <ItemGroup> <None Include="$(MSBuildThisFileDirectory)/CodeAnalysis/stylecop.json" Link="stylecop.json" CopyToOutputDirectory="Never"/> <PackageReference Include="StyleCop.Analyzers" Version="1.*" PrivateAssets="all" /> </ItemGroup> <PropertyGroup> <CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)/CodeAnalysis/stylecop.ruleset</CodeAnalysisRuleSet> <!-- Enable XML docs generating--> <GenerateDocumentationFile>true</GenerateDocumentationFile> <!-- Enable C# 7.x features --> <LangVersion>latest</LangVersion> <!-- default base version --> <BaseVersion Condition="'$(BaseVersion)' == ''">0.0.0</BaseVersion> <!-- default build number and format --> <BuildNumber Condition="'$(BuildNumber)' == ''">0</BuildNumber> <BuildNumber>$([System.String]::Format('{0:0000}',$(BuildNumber)))</BuildNumber> <!-- default version suffix --> <VersionSuffix Condition="'$(VersionSuffix)' == ''">local</VersionSuffix> <!-- empty version suffix instead of 'prod' --> <VersionSuffix Condition="'$(VersionSuffix)' == 'prod'"></VersionSuffix> <!-- format version prefix --> <VersionPrefix>$(BaseVersion).$(BuildNumber)</VersionPrefix> <!-- disable IsPackable by default --> <IsPackable>false</IsPackable> <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl> <Company>Contoso</Company> <Copyright>Copyright $([System.DateTime]::Now.Date.Year) Contoso Ltd</Copyright> </PropertyGroup> </Project> 

Majelis


Perlu disebutkan langsung bahwa saya dan kolega saya sudah memiliki pengalaman yang cukup sukses dalam menggunakan sistem pembangunan yang berbeda. Dan alih-alih menimbang alat yang ada dengan fungsi yang sama sekali tidak seperti biasanya, diputuskan untuk membuat yang lain, khusus untuk proses baru kami, dan meninggalkan yang lama sendirian untuk melakukan tugasnya sebagai bagian dari proyek warisan. Ide perbaikannya adalah keinginan untuk mendapatkan alat yang akan mengubah kode menjadi gambar buruh pelabuhan yang memenuhi semua persyaratan kami, menggunakan proses standar tunggal, sambil menghilangkan kebutuhan pengembang untuk menyelami seluk-beluk perakitan, tetapi mempertahankan kemungkinan beberapa penyesuaian.


Pemilihan kerangka kerja yang sesuai telah dimulai. Berdasarkan persyaratan reproduksibilitas hasil, baik pada mesin yang dibangun dengan Linux maupun pada mesin Windows dari pengembang mana pun, lintas platform nyata dan minimum ketergantungan yang ditetapkan menjadi syarat utama. Pada waktu yang berbeda, saya berhasil mengenal beberapa kerangka kerja perakitan untuk pengembang .NET dengan cukup baik: dari MSBuild dan konfigurasi XML yang mengerikan, yang kemudian diterjemahkan ke dalam Psake (Powershell), ke FAKE eksotis (F #). Tapi kali ini aku menginginkan sesuatu yang segar dan terang. Selain itu, sudah diputuskan bahwa perakitan dan pengujian harus dilakukan sepenuhnya dalam lingkungan wadah yang terisolasi, jadi saya tidak berencana untuk menjalankan apa pun selain perintah Docker CLI dan Git, yaitu, sebagian besar proses seharusnya dijelaskan dalam Dockerfile.
Pada saat itu, FAKE 5 dan Cake untuk .NET Core masih belum siap, jadi dengan cross-platform, proyek-proyek ini begitu-begitu. Tapi PowerShell 6 Core saya yang terkasih sudah dirilis, dan saya menggunakannya sepenuhnya. Oleh karena itu, saya memutuskan untuk beralih ke Psake lagi, dan ketika saya berbalik, saya menemukan sebuah proyek Invoke-Build yang menarik , yang merupakan pemikiran ulang dari Psake dan, seperti yang penulis tunjukkan, adalah sama, hanya lebih baik dan lebih mudah. Begitulah. Saya tidak akan membahasnya secara terperinci dalam kerangka artikel ini, saya hanya akan mencatat bahwa kekompakan menyuap saya di dalamnya jika semua fungsi dasar untuk kelas produk ini tersedia:


  • Urutan tindakan dijelaskan oleh serangkaian tugas yang saling terkait (tugas), yang dapat dikontrol menggunakan saling ketergantungan mereka dan kondisi tambahan.
  • Ada beberapa bantuan praktis, misalnya, exec {} untuk menangani kode keluar aplikasi konsol dengan benar.
  • Pengecualian atau berhenti menggunakan Ctrl + C akan diproses dengan benar di blok Exit-Build built-in khusus. Misalnya, di sana Anda dapat menghapus semua file sementara, lingkungan pengujian, atau menggambar laporan yang enak dipandang.


Dockerfile Umum


Dockerfile sendiri dan perakitan yang menggunakan docker build memberikan kemampuan parameterisasi yang cukup lemah, dan fleksibilitas alat-alat ini hampir tidak sedikit lebih tinggi dari pada pegangan sekop. Selain itu, ada banyak cara untuk membuat gambar yang "salah", terlalu besar, terlalu tidak aman, terlalu tidak intuitif, atau hanya tidak dapat diprediksi. Untungnya, dokumentasi Microsoft sudah menawarkan beberapa contoh Dockerfile , yang memungkinkan Anda untuk dengan cepat memahami konsep dasar dan membuat Dockerfile pertama Anda, secara bertahap memperbaikinya nanti. Dia sudah menggunakan pola multi-tahap dan membangun gambar " Test Runner " khusus untuk menjalankan tes.


Pola dan argumen multi-tahap


Langkah pertama adalah memecah tahap perakitan menjadi yang lebih kecil dan menambahkan yang baru. Jadi, perlu menyoroti peluncuran dotnet build sebagai tahap terpisah, karena untuk proyek yang hanya berisi pustaka, tidak masuk akal untuk menjalankan dotnet publish . Sekarang, atas kebijakan kami, kami hanya dapat menjalankan tahapan perakitan yang diperlukan menggunakan
dotnet build --target <name>
Misalnya, di sini kami mengumpulkan proyek yang hanya berisi perpustakaan. Artefak di sini hanya paket NuGet, yang berarti tidak masuk akal untuk mengumpulkan gambar runtime.



Atau kita sudah membangun layanan, tetapi dari cabang fitur. Kami tidak memerlukan artefak dari perakitan semacam itu sama sekali, hanya penting untuk lulus tes dan pemeriksaan kesehatan.



Hal selanjutnya yang harus dilakukan adalah parameterisasi penggunaan gambar dasar. Untuk beberapa waktu sekarang, di Dockerfile, arahan ARG dapat ditempatkan di luar tahap build, dan nilai yang ditransfer dapat digunakan atas nama gambar dasar.


 ARG DOTNETCORE_VERSION=2.2 ARG ALPINE_VERSION= ARG BUILD_BASE=mcr.microsoft.com/dotnet/core/sdk:${DOTNETCORE_VERSION}-alpine${ALPINE_VERSION} ARG RUNTIME_BASE=mcr.microsoft.com/dotnet/core/runtime:${DOTNETCORE_VERSION}-alpine${ALPINE_VERSION} FROM ${BUILD_BASE} AS restore ... FROM ${RUNTIME_BASE} AS runtime ... 

Jadi kami mendapat kesempatan baru dan pada pandangan pertama, dan tidak jelas. Pertama, jika kita ingin membangun sebuah gambar dengan aplikasi ASP.NET Core, maka gambar runtime akan membutuhkan yang berbeda: mcr.microsoft.com/dotnet/core/aspnet . Parameter dengan gambar dasar non-standar harus disimpan dalam konfigurasi repositori SolutionInfo.props dan meneruskannya sebagai argumen selama perakitan. Kami juga mempermudah pengembang untuk menggunakan versi lain dari .NET Core images: preview, misalnya, atau bahkan yang khusus (Anda tidak pernah tahu!).


Kedua, kemampuan untuk "memperluas" Dockerfile bahkan lebih menarik, setelah membuat bagian dari operasi di perakitan lain, yang hasilnya akan diambil sebagai dasar saat menyiapkan gambar runtime. Sebagai contoh, beberapa layanan kami menggunakan JavaScript dan Vue.js, kode yang akan kami siapkan dalam gambar terpisah, cukup menambahkan Dockerfile "yang meluas" ke repositori:


 ARG DOTNETCORE_VERSION=2.2 ARG ALPINE_VERSION= ARG RUNTIME_BASE=mcr.microsoft.com/dotnet/core/aspnet:${DOTNETCORE_VERSION}-alpine${ALPINE_VERSION} FROM node:alpine AS install WORKDIR /build COPY package.json . RUN npm install FROM install AS src COPY [".babelrc", ".eslintrc.js", ".stylelintrc", "./"] COPY ClientApp ./ClientApp FROM src AS publish RUN npm run build-prod FROM ${RUNTIME_BASE} AS appbase COPY --from=publish /build/wwwroot/ /app/wwwroot/ 

Mari kumpulkan gambar ini dengan tag, yang akan kami sampaikan ke tahap perakitan gambar runtime dari layanan ASP.NET sebagai argumen untuk RUNTIME_BASE. Jadi, Anda dapat memperluas perakitan sebanyak yang Anda suka, termasuk, Anda bisa membuat parameter apa yang tidak bisa Anda lakukan hanya dalam docker build . Ingin mengukur parameter penambahan Volume? Mudah:


 ARG DOTNETCORE_VERSION=2.2 ARG ALPINE_VERSION= ARG RUNTIME_BASE=mcr.microsoft.com/dotnet/core/aspnet:${DOTNETCORE_VERSION}-alpine${ALPINE_VERSION} FROM ${RUNTIME_BASE} AS runtime ARG VOLUME VOLUME ${VOLUME} 

Kami memulai perakitan Dockerfile ini sebanyak yang kami ingin tambahkan arahan VOLUME. Kami menggunakan gambar yang dihasilkan sebagai basis untuk layanan.


Menjalankan tes


Alih-alih menjalankan tes langsung di tahap perakitan, lebih tepat dan lebih mudah untuk melakukan ini dalam wadah "Test Runner" khusus. Secara singkat menyampaikan inti dari pendekatan ini, saya perhatikan bahwa ini memungkinkan Anda untuk:


  • Lakukan semua peluncuran terjadwal, bahkan jika salah satu dari itu crash;
  • Pasang direktori sistem file host ke dalam wadah untuk menerima laporan pengujian, yang sangat penting ketika membangun sistem CI;
  • Jalankan pengujian dalam lingkungan sementara dengan mengirimkan nama jaringannya ke docker run --network <test_network_name> .

Paragraf terakhir berarti bahwa kita sekarang dapat menjalankan tidak hanya tes unit, tetapi juga tes integrasi. Kami menggambarkan lingkungan, misalnya, di docker-compose.yaml , dan menjalankannya untuk seluruh build. Sekarang Anda dapat memeriksa interaksi dengan database atau layanan kami yang lain, dan menyimpan log dari mereka jika Anda membutuhkannya untuk analisis.


Kami selalu memeriksa gambar runtime yang dihasilkan untuk melewati pemeriksaan kesehatan, yang juga merupakan semacam tes. Lingkungan pengujian sementara mungkin berguna di sini jika layanan yang diuji memiliki ketergantungan pada lingkungannya.


Saya juga mencatat bahwa pendekatan dengan wadah pelari berkumpul di panggung dengan dotnet build akan berfungsi dengan baik untuk meluncurkan dotnet publish , dotnet pack dotnet nuget push dan dotnet nuget push . Ini akan memungkinkan kami untuk menyimpan artefak rakitan secara lokal.


Pemeriksaan Kesehatan dan Dependensi OS


Cukup cepat menjadi jelas bahwa layanan standar kami akan tetap unik dengan caranya sendiri. Mereka mungkin memiliki persyaratan berbeda untuk paket yang sudah diinstal dari sistem operasi di dalam gambar dan cara yang berbeda untuk memeriksa cek kesehatan. Dan jika curl cocok untuk memeriksa status aplikasi Web, maka untuk backend gRPC atau, apalagi, layanan tanpa kepala, itu tidak akan berguna, dan itu juga akan menjadi paket tambahan dalam wadah.


Untuk memberi pengembang kesempatan untuk menyesuaikan gambar dan memperluas konfigurasinya, kami menggunakan perjanjian pada beberapa skrip khusus yang dapat didefinisikan ulang dalam repositori:


 .scripts β”œβ”€β”€ healthcheck.sh β”œβ”€β”€ run.sh └── runtime-deps.sh 

Skrip healthcheck.sh berisi perintah yang diperlukan untuk memeriksa status:


  • Untuk web menggunakan curl:


     #!/bin/ash set –e curl -sIf -o /dev/null -w "%{http_code}\n" 127.0.0.1/health || exit 1 

  • Layanan lain yang menggunakan utilitas klien kami sendiri:


     #!/bin/ash set –e healthcheck || exit 1 


Menggunakan runtime-deps.sh , dependensi diinstal dan, jika diperlukan, tindakan lain apa pun pada OS dasar dilakukan yang diperlukan untuk fungsi normal aplikasi di dalam wadah. Contoh umum:


  • Untuk aplikasi web:


     #!/bin/ash apk add --no-cache curl icu-libs 

  • Untuk layanan gRPC:


     #!/bin/ash apk add --no-cache libc6-compat 


Dengan demikian, cara untuk mengelola dependensi dan memeriksa keadaan adalah standar, tetapi ada ruang untuk fleksibilitas. Adapun run.sh , maka itu lebih jauh.


Script entrypoint


Saya yakin bahwa setiap orang yang setidaknya sekali menulis Dockerfile mereka bertanya-tanya arahan mana yang harus digunakan - CMD atau ENTRYPOINT . Selain itu, tim-tim ini juga memiliki dua opsi sintaks, yang secara dramatis mempengaruhi hasil. Saya tidak akan menjelaskan perbedaannya secara rinci, mengulangi setelah mereka yang telah mengklarifikasi semuanya . Saya hanya menyarankan mengingat bahwa dalam 99% situasi sudah benar untuk menggunakan ENTRYPOINT dan sintaks exec:


ENTRYPOINT ["/ path / to / executable"]


Jika tidak, aplikasi yang diluncurkan tidak akan dapat memproses perintah OS dengan benar, seperti SIGTERM, dll., Dan Anda juga bisa mendapat masalah dalam bentuk proses zombie dan semua yang terkait dengan masalah PID 1 . Tetapi bagaimana jika Anda ingin memulai wadah tanpa meluncurkan aplikasi? Ya, Anda dapat mengganti titik masuk:
docker run --rm -it --entrypoint ash <image_name> <params>
Itu tidak terlihat terlalu nyaman dan intuitif, bukan? Tetapi ada kabar baik: Anda bisa berbuat lebih baik! Yaitu, gunakan skrip entrypoint . Skrip semacam itu memungkinkan Anda untuk melakukan inisialisasi kompleks ( contoh ) sewenang-wenang, pemrosesan parameter, dan apa pun yang Anda inginkan.


Dalam kasus kami, secara default, skenario fungsional paling sederhana, tetapi pada saat yang sama digunakan:


 #!/bin/sh set -e if [ ! -z "$1" ] && $(command -v $1 >/dev/null 2>&1) then exec $@ else exec /usr/bin/dotnet /app/${ENTRY_PROJECT}.dll $@ fi 

Ini memungkinkan Anda untuk mengontrol peluncuran wadah dengan sangat intuitif:
docker run <image> env - hanya mengeksekusi env dalam gambar, menunjukkan variabel lingkungan.
docker run <image> -param1 value1 - memulai layanan dengan argumen yang ditentukan.


Secara terpisah, Anda perlu memperhatikan perintah exec : kehadirannya sebelum memanggil aplikasi yang dapat dieksekusi akan memastikan bahwa ia bekerja dengan PID 1 yang didambakan dalam wadah Anda.


Apa lagi


Tentu saja, lebih dari satu setengah tahun penggunaan, sistem build telah mengakumulasikan banyak fungsi yang berbeda. Selain mengelola kondisi peluncuran berbagai tahap, bekerja dengan penyimpanan artefak, versi, dan fitur lainnya, "standar" wadah kami juga dikembangkan. Itu diisi dengan atribut penting yang membuatnya lebih mudah diprediksi dan secara administratif nyaman:


  • Semua label gambar yang diperlukan diinstal: versi, angka revisi, tautan ke dokumentasi, penulis dan lain-lain.
  • Dalam wadah runtime, konfigurasi NLog didefinisikan ulang sehingga setelah publikasi semua log segera disajikan dalam bentuk terstruktur menggunakan json, versi yang diversi.
  • Aturan analisis statis dan standar lainnya secara otomatis diperbarui.

Alat seperti itu, tentu saja, selalu dapat ditingkatkan dan dikembangkan. Itu semua tergantung kebutuhan dan imajinasi. Sebagai contoh, selain segalanya, dimungkinkan untuk mengemas utilitas klien tambahan ke dalam gambar. Pengembang dapat dengan mudah menempatkan mereka di gambar, menentukan dalam file konfigurasi hanya nama utilitas yang diperlukan dan nama proyek .NET dari mana ia harus dirakit (misalnya, healthcheck kami).


Kesimpulan


Dijelaskan di sini hanya bagian dari pendekatan terpadu untuk standardisasi. , , , , , . . , .


, Linux , - . , , . , , , Code Style, , .


, ! , Β« Β», , . , Docker .

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


All Articles