
Dalam artikel ini, saya akan berbicara tentang pengalaman saya "membungkus" aplikasi Laravel dalam wadah Docker sehingga pengembang frontend dan backend dapat bekerja secara lokal dengannya, dan meluncurkannya pada produksi sesederhana mungkin. Selain itu, CI akan secara otomatis menjalankan analisis kode statis, tes phpunit
, dan membuat gambar.
"Dan apa, sebenarnya, kerumitan itu?" - Anda bisa mengatakan, dan Anda akan sebagian benar. Faktanya adalah bahwa cukup banyak diskusi dalam komunitas berbahasa Rusia dan berbahasa Inggris dikhususkan untuk topik ini, dan saya akan membagi secara kondisional hampir semua utas yang dipelajari ke dalam kategori berikut:
- "Aku menggunakan buruh pelabuhan untuk pengembangan lokal. Aku meletakkan laradock dan aku tidak tahu masalahnya." Keren, tapi bagaimana dengan peluncuran otomatisasi dan produksi?
- "Saya mengumpulkan satu kontainer (monolit) berdasarkan
fedora:latest
(~ 230 Mb), memasukkan semua layanan (nginx, db, cache, dll) ke dalamnya, jalankan semua yang ada di dalam supervisor." Juga luar biasa, mudah untuk memulai, tetapi bagaimana dengan ideologi "satu wadah - satu proses"? Bagaimana dengan penyeimbangan dan manajemen proses? Berapa ukuran gambar? - "Ini potongan-potongan konfigurasi, bumbui dengan kutipan dari skrip-sh, tambahkan nilai sihir env, gunakan." Terima kasih, tetapi bagaimana dengan setidaknya satu contoh hidup yang dapat saya mainkan dan mainkan sepenuhnya?
Semua yang Anda baca di bawah ini adalah pengalaman subjektif yang tidak berpura-pura sebagai kebenaran tertinggi. Jika Anda memiliki tambahan atau indikasi ketidakakuratan - selamat datang di komentar.
Untuk yang tidak sabar - tautan ke repositori , tiruan yang Anda dapat memulai aplikasi Laravel dengan satu perintah. Juga tidak sulit untuk menjalankannya pada pemilik peternakan yang sama, dengan benar "menghubungkan" wadah-wadah, atau menggunakan versi belanjaan docker-compose.yml
sebagai titik awal.
Bagian teoretis
Alat apa yang akan kita gunakan dalam pekerjaan kita, dan apa yang akan kita fokuskan? Pertama-tama, kita perlu menginstal host:
docker
- pada saat penulisan, saya menggunakan versi 18.06.1-ce
docker-compose
- ini mengatasi dengan menghubungkan kontainer dan menyimpan nilai-nilai lingkungan yang diperlukan; versi 1.22.0
make
- Anda mungkin akan terkejut, tetapi sangat cocok dengan konteks bekerja dengan buruh pelabuhan
Anda dapat curl -fsSL get.docker.com | sudo sh
docker
pada sistem mirip- debian
dengan perintah curl -fsSL get.docker.com | sudo sh
curl -fsSL get.docker.com | sudo sh
, tetapi docker-compose
lebih baik untuk menginstal menggunakan pip
, karena versi terbaru tinggal di repositori-nya ( apt
jauh di belakang, sebagai aturan).
Ini melengkapi daftar dependensi. Apa yang akan Anda gunakan untuk bekerja dengan kode sumber - phpstorm
, netbeans
atau dead vim
- terserah Anda.
Selanjutnya adalah QA dadakan dalam konteks desain gambar (saya tidak takut pada kata ini) :
T: Gambar dasar - mana yang lebih baik untuk dipilih?
A: Yang "lebih tipis", tanpa embel-embel. Atas dasar alpine
(~ 5 Mb), Anda dapat mengumpulkan apa pun yang diinginkan hati Anda, tetapi kemungkinan besar Anda harus bermain dengan perakitan layanan dari sumbernya. Sebagai alternatif - jessie-slim
(~ 30 Mb) . Atau gunakan yang paling sering digunakan pada proyek Anda.
T: Mengapa bobot gambar penting?
A: Penurunan volume lalu lintas, penurunan probabilitas kesalahan saat mengunduh (lebih sedikit data - kurang probabilitas), penurunan tempat yang dikonsumsi. Aturan "Gravity is reliable" (Β© "Snatch") tidak berfungsi dengan baik di sini.
T: Tapi teman saya %friend_name%
mengatakan bahwa gambar "monolitik" dengan semua-semua dependensi adalah cara terbaik.
A: Mari kita hitung saja. Aplikasi ini memiliki 3 dependensi - PG, Redis, PHP. Dan Anda ingin menguji bagaimana perilakunya dalam bundel versi berbeda dari dependensi ini. PG - versi 9.6 dan 10, Redis - 3.2 dan 4.0, PHP - 7.0 dan 7.2. Seandainya setiap kecanduan adalah gambar yang terpisah - Anda memerlukan 6 di antaranya, yang bahkan tidak perlu Anda kumpulkan - semuanya sudah siap dan terletak di hub.docker.com
. Jika, karena alasan ideologis, semua dependensi "dikemas" dalam satu wadah, Anda harus merakitnya dengan pena ... 8 kali? Sekarang tambahkan kondisi yang Anda masih ingin bermain dengan opcache
. Dalam kasus dekomposisi, ini hanyalah perubahan pada tag gambar yang digunakan. Monolith lebih mudah dijalankan dan dipelihara, tetapi itu adalah jalan ke mana-mana.
T: Mengapa penyelia dalam wadah itu jahat?
A: Karena PID 1
. Jika Anda tidak ingin banyak masalah dengan proses zombie dan memiliki kemampuan untuk "menambah kapasitas" secara fleksibel jika perlu - cobalah menjalankan satu proses per wadah. Pengecualian khusus adalah nginx
dengan pekerjanya dan php-fpm
, yang memiliki kemampuan untuk menghasilkan proses, tetapi harus tahan dengan ini (apalagi, mereka tidak buruk bereaksi terhadap SIGTERM
, cukup benar "membunuh" pekerjanya). Dengan meluncurkan semua iblis sebagai pengawas, Anda hampir pasti akan menemui kesulitan. Meskipun, dalam beberapa kasus, sulit untuk melakukannya tanpanya, tetapi ini sudah merupakan pengecualian.
Setelah memutuskan pendekatan utama, mari beralih ke aplikasi kita. Itu harus dapat:
web|api
- berikan static dengan nginx
, dan hasilkan konten dinamis dengan fpm
scheduler
- menjalankan penjadwal tugas asliqueue
- memproses pekerjaan dari antrian
Satu set dasar yang dapat diperluas jika perlu. Sekarang mari kita beralih ke gambar yang harus kita kumpulkan agar aplikasi kita "lepas landas" (nama kode mereka diberikan dalam tanda kurung):
PHP + PHP-FPM
( app ) - lingkungan di mana kode kita akan dieksekusi. Karena versi PHP dan FPM akan sama untuk kami - kami mengumpulkannya dalam satu gambar. Jadi lebih mudah dikelola dengan konfigurasi, dan komposisi paket akan sama. Tentu saja - FPM dan proses aplikasi akan berjalan dalam wadah yang berbedanginx
( nginx ) - yang tidak akan mengganggu pengiriman konfigurasi dan modul opsional untuk nginx
- kami akan mengumpulkan gambar terpisah dengannya. Karena ini adalah layanan terpisah, ia memiliki file buruh pelabuhan dan konteksnya sendiri- Sumber aplikasi ( sumber ) - sumber akan dikirim menggunakan gambar yang terpisah, dengan memasukkan
volume
dalam wadah dengan aplikasi. Gambar dasar adalah alpine
, di dalam hanya ada sumber dengan dependensi yang diinstal dan dikumpulkan menggunakan aset webpack (membangun artefak)
Layanan pengembangan lainnya diluncurkan dalam wadah, menariknya dari hub.docker.com
; pada produksi, di sisi lain - mereka berjalan di server yang terpisah, berkerumun. Yang tersisa bagi kita adalah memberi tahu aplikasi (melalui lingkungan) di mana alamat / port dan dengan rincian mana perlu mengetuk mereka. Bahkan yang lebih keren adalah menggunakan penemuan-layanan untuk tujuan ini, tetapi tidak untuk saat ini.
Setelah memutuskan pada bagian teoretis, saya mengusulkan pindah ke bagian selanjutnya.
Bagian praktis
Saya sarankan mengatur file dalam repositori sebagai berikut:
. βββ docker # - β βββ app β β βββ Dockerfile β β βββ ... β βββ nginx β β βββ Dockerfile β β βββ ... β βββ sources β βββ Dockerfile β βββ ... βββ src # β βββ app β βββ bootstrap β βββ config β βββ artisan β βββ ... βββ docker-compose.yml # Compose- βββ Makefile βββ CHANGELOG.md βββ README.md
Anda dapat membiasakan diri dengan struktur dan file dengan mengklik tautan ini .
Untuk membangun layanan, Anda dapat menggunakan perintah:
$ docker build \ --tag %local_image_name% \ -f ./docker/%service_directory%/Dockerfile ./docker/%service_directory%
Satu-satunya perbedaan adalah perakitan gambar dengan sumber - untuk itu, konteks perakitan (argumen ekstrem) ./src
diatur ke ./src
.
Aturan untuk menamai gambar di registri lokal merekomendasikan menggunakan yang digunakan pembuat docker-compose
%root_directory_name%_%service_name%
secara default, yaitu: %root_directory_name%_%service_name%
. Jika direktori proyek bernama my-awesome-project
, dan layanan ini bernama redis
, maka nama gambar (lokal) masing-masing lebih baik untuk memilih my-awesome-project_redis
.
Untuk mempercepat proses pembuatan, Anda dapat memberi tahu buruh pelabuhan untuk menggunakan cache dari gambar yang dirakit sebelumnya, dan untuk ini, --cache-from %full_registry_name%
peluncuran --cache-from %full_registry_name%
. Dengan demikian, daemon buruh pelabuhan akan terlihat sebelum memulai instruksi tertentu di Dockerfile - apakah sudah berubah? Dan jika tidak (hash menyatu) - ia akan melewati instruksi, menggunakan layer yang sudah disiapkan dari gambar, yang akan Anda kirim untuk digunakan sebagai cache. Hal ini tidak buruk sehingga akan membangun kembali prosesnya, terutama jika tidak ada yang berubah :)
Perhatikan skrip ENTRYPOINT
untuk meluncurkan wadah aplikasi.
Gambar lingkungan untuk meluncurkan aplikasi (aplikasi) dikumpulkan dengan mempertimbangkan fakta bahwa itu akan bekerja tidak hanya pada produksi, tetapi juga secara lokal, pengembang perlu berinteraksi secara efektif dengannya. Memasang dan menghapus dependensi composer
, menjalankan unit
test, log tail
dan menggunakan alias yang sudah dikenal ( php /app/artisan
β art
, composer
β c
) harus tanpa rasa tidak nyaman. Selain itu - ini juga akan digunakan untuk menjalankan unit
test dan analisa kode statis ( phpstan
dalam kasus kami) di CI. Itulah sebabnya Dockerfile-nya, misalnya, berisi jalur instalasi xdebug
, tetapi modul itu sendiri tidak diaktifkan (hanya diaktifkan menggunakan CI).
Juga untuk composer
paket hirak/prestissimo
, yang sangat meningkatkan pemasangan semua dependensi.
Saat produksi, kami memasang konten direktori /src
dari gambar dengan sumber (sumber) di dalamnya ke direktori /app
. Untuk pengembangan, kami βroll overβ direktori lokal dengan sumber aplikasi ( -v "$(pwd)/src:/app:rw"
).
Dan di sini terletak satu kompleksitas - ini adalah hak akses ke file yang dibuat dari wadah. Faktanya adalah bahwa secara default proses yang berjalan di dalam wadah mulai dari root ( root:root
), file yang dibuat oleh proses ini (cache, log, sesi, dll) - juga, dan sebagai hasilnya - Anda tidak memiliki sesuatu yang "lokal" dengan mereka Anda dapat melakukannya tanpa menjalankan sudo chown -R $(id -u):$(id -g) /path/to/sources
.
Sebagai satu solusi, gunakan fixuid , tetapi solusi ini mudah. Menurut saya cara terbaik untuk USER_ID
lokal dan GROUP_ID
- GROUP_ID
di dalam wadah, dan memulai proses dengan nilai-nilai ini . Secara default, mengganti nilai 1000:1000
(nilai default untuk pengguna lokal pertama) menyingkirkan panggilan $(id -u):$(id -g)
, dan jika perlu, Anda selalu dapat menimpanya ( $ USER_ID=666 docker-compose up -d
) atau letakkan file docker-compose di file .env
.
Juga, ketika php-fpm
diluncurkan secara lokal php-fpm
lupa untuk menonaktifkan opcache
darinya - jika tidak ada banyak "ya, apa-apaan! Anda akan diberikan.
Untuk koneksi "langsung" ke redis dan postgres, saya melemparkan port tambahan "keluar" (masing-masing 15432
dan 15432
), sehingga tidak ada masalah dengan "menghubungkan dan melihat apa dan bagaimana sebenarnya" pada prinsipnya.
Saya menjaga wadah dengan app
nama kode berjalan (- --command keep-alive.sh
) untuk tujuan akses mudah ke aplikasi.
Berikut adalah beberapa contoh pemecahan masalah sehari-hari dengan docker-compose
:
Operasi | Menjalankan perintah |
---|
Instal paket composer | $ docker-compose exec app composer require package/name |
Menjalankan phpunit | $ docker-compose exec app php ./vendor/bin/phpunit --no-coverage |
Instal semua dependensi simpul | $ docker-compose run --rm node npm install |
Instal paket simpul | $ docker-compose run --rm node npm i package_name |
Meluncurkan pembangunan kembali aset secara langsung | $ docker-compose run --rm node npm run watch |
Anda dapat menemukan semua detail peluncuran di file docker-compose.yml .
Choi make
hidup!
Mengetik perintah yang sama setiap kali menjadi membosankan setelah yang kedua kalinya, dan karena programmer pada dasarnya adalah makhluk yang malas, mari masuk ke "otomatisasi" mereka. Menyimpan seperangkat naskah adalah sebuah pilihan, tetapi tidak semenarik Makefile
tunggal, terutama karena penerapannya dalam pengembangan modern sangat diremehkan.
Manual lengkap dalam bahasa Rusia dapat Anda temukan di tautan ini .
Mari kita lihat bagaimana tampilan make
di root repositori:
[user@host ~/projects/app] $ make help Show this help app-pull Application - pull latest Docker image (from remote registry) app Application - build Docker image locally app-push Application - tag and push Docker image into remote registry sources-pull Sources - pull latest Docker image (from remote registry) sources Sources - build Docker image locally sources-push Sources - tag and push Docker image into remote registry nginx-pull Nginx - pull latest Docker image (from remote registry) nginx Nginx - build Docker image locally nginx-push Nginx - tag and push Docker image into remote registry pull Pull all Docker images (from remote registry) build Build all Docker images push Tag and push all Docker images into remote registry login Log in to a remote Docker registry clean Remove images from local registry --------------- --------------- up Start all containers (in background) for development down Stop all started for development containers restart Restart all started for development containers shell Start shell into application container install Install application dependencies into application container watch Start watching assets for changes (node) init Make full application initialization (install, seed, build assets) test Execute application tests Allowed for overriding next properties: PULL_TAG - Tag for pulling images before building own ('latest' by default) PUBLISH_TAGS - Tags list for building and pushing into remote registry (delimiter - single space, 'latest' by default) Usage example: make PULL_TAG='v1.2.3' PUBLISH_TAGS='latest v1.2.3 test-tag' app-push
Dia sangat pandai dalam tujuan adiktif. Misalnya, untuk mulai watch
( docker-compose run --rm node npm run watch
), Anda perlu aplikasi "dinaikkan" - Anda hanya perlu menentukan target up
sebagai tergantung - dan Anda tidak perlu khawatir lupa melakukan hal ini sebelum menelepon watch
- make
dirinya akan melakukan segalanya untukmu. Hal yang sama berlaku untuk menjalankan tes dan analisis statis, misalnya, sebelum melakukan perubahan - jalankan make test
dan semua keajaiban akan terjadi untuk Anda!
Tidak perlu dikatakan, Anda tidak perlu khawatir tentang mengumpulkan gambar, mengunduhnya, menentukan --cache-from
dan hampir semuanya?
Anda dapat melihat konten Makefile
di tautan ini .
Bagian otomatis
Mari kita lanjutkan ke bagian akhir artikel ini - ini adalah otomatisasi proses memperbarui gambar di Docker Registry. Meskipun dalam contoh saya GitLab CI digunakan - untuk mentransfer ide ke layanan integrasi lain, saya pikir itu akan sangat mungkin.
Pertama-tama, kami akan menentukan penamaan tag gambar yang digunakan:
Nama tag | Tujuan |
---|
latest | Gambar dikumpulkan dari cabang master . Keadaan kode adalah yang terbaru, tetapi belum siap untuk masuk ke rilis |
some-branch-name | Gambar dikumpulkan pada brunch some-branch-name . Dengan demikian, kita dapat "meluncurkan" perubahan dalam lingkungan apa pun yang hanya diterapkan dalam kerangka kerja brunch tertentu sebelum menggabungkannya dengan master -light - cukup untuk "meregangkan" gambar dengan tag ini. Dan - ya, perubahan mungkin menyangkut kode dan gambar semua layanan secara umum! |
vX.XX | Sebenarnya, rilis aplikasi (gunakan untuk menyebarkan versi tertentu) |
stable | Alias, untuk tag dengan rilis terbaru (gunakan untuk menggunakan versi stabil terbaru) |
Rilis ini dilakukan dengan menerbitkan tag dalam format vX.XX
Untuk mempercepat pembangunan, caching direktori ./src/vendor
dan ./src/node_modules
+ --cache-from
untuk docker build
, dan terdiri dari tahapan berikut:
Nama panggung | Tujuan |
---|
prepare | Fase persiapan - perakitan gambar semua layanan kecuali gambar dengan sumbernya |
test | Menguji aplikasi (menjalankan phpunit , penganalisa kode statis) menggunakan gambar yang dikumpulkan pada tahap persiapan |
build | Menginstal semua dependensi composer ( --no-dev ), merakit assets webpack , dan merakit gambar dengan kode sumber termasuk artefak yang diterima ( vendor/* , app.js , app.css ) |

Perakitan pada cabang master
menghasilkan push
dengan tag master
dan latest
Rata-rata, semua tahap perakitan memakan waktu 4 menit , yang merupakan hasil yang cukup bagus (pelaksanaan tugas paralel adalah segalanya bagi kami).
Anda dapat membiasakan diri dengan konten konfigurasi ( .gitlab-ci.yml
) dari kolektor di tautan ini .
Alih-alih sebuah kesimpulan
Seperti yang Anda lihat, mengorganisasikan pekerjaan dengan aplikasi php (menggunakan Laravel
sebagai contoh) menggunakan Docker tidak begitu sulit. Sebagai ujian, Anda dapat melakukan fork repositori , dan mengganti semua kemunculan tarampampam/laravel-in-docker
dengan milik Anda - coba semuanya "hidup" sendiri.
Untuk peluncuran lokal - jalankan hanya 2 perintah:
$ git clone https://gitlab.com/tarampampam/laravel-in-docker.git ./laravel-in-docker && cd $_ $ make init
Kemudian buka http://127.0.0.1:9999
di browser favorit Anda.
... merebut peluang
Saat ini saya sedang mengerjakan proyek TL "autocode", dan kami sedang mencari pengembang dan administrator sistem php yang berbakat (kantor pengembangan berlokasi di Yekaterinburg). Jika Anda menganggap diri Anda sebagai yang pertama atau kedua - tulis surat SDM kami dengan teks "Saya ingin menjadi tim pengembangan, lanjutkan:% link_on_summary%" ke email hr@avtocod.ru
, kami membantu dengan relokasi.