Perbezaan .env, env_file dan environment Docker Compose
Ketahui perbezaan antara .env, env_file dan environment dalam Docker Compose. Fahami susunan keutamaan dan cara mengurus rahsia sensitif dengan selamat dalam container anda.
Tiga perkara yang dipanggil sebagai fail env
Docker Compose mempunyai tiga mekanisme berasingan dengan nama yang mengelirukan. Fail .env mengisi pemegang tempat ${VARIABLE} di dalam compose.yaml itu sendiri, selepas Compose membaca YAML dan sebelum ia mengesahkan atau menjalankan apa-apa. Atribut env_file: memuatkan fail pasangan kunci/nilai ke dalam persekitaran container. Atribut environment: menetapkan pemboleh ubah pada container secara terus, yang ditulis di dalam fail compose. Ia tidak boleh ditukar ganti, dan apabila dua daripadanya menetapkan kunci yang sama, pemenangnya ditentukan oleh susunan keutamaan yang didokumenkan.
Panduan ini menunjukkan cara setiap satunya berfungsi, membuktikan keutamaan dengan arahan yang boleh anda jalankan, kemudian merangkumi bahagian yang lebih penting: pemboleh ubah persekitaran boleh dibaca oleh sesiapa sahaja yang boleh menjalankan docker inspect, jadi kata laluan tidak sepatutnya diletakkan di dalamnya. Jika anda baru menggunakan fail compose secara umum, mulakan dengan Asas Docker Compose pada VPS dan kembali ke sini untuk konfigurasi.
Fail .env adalah untuk fail compose, bukan untuk container
Cipta satu direktori dan letakkan dua fail di dalamnya.
mkdir -p ~/envdemo && cd ~/envdemo
printf 'ALPINE_TAG=3.20\n' > .envservices:
demo:
image: alpine:${ALPINE_TAG}
command: printenv ALPINE_TAGSekarang, tanya Compose perkara yang sebenarnya telah dihurai (parsed).
docker compose configOutput menunjukkan image: alpine:3.20. Penanda tempat (placeholder) telah hilang kerana interpolasi berlaku semasa Compose memuatkan fail tersebut. Compose mencari .env dalam direktori projek: direktori yang diberikan dengan --project-directory jika anda menetapkannya, jika tidak, direktori bagi fail pertama yang diluluskan dengan -f, atau direktori semasa anda. Ia menggantikan penanda tempat ${NAME} yang ditemui dalam nilai YAML. Kunci (keys) tidak akan diinterpolasi, dan $$ yang digandakan akan menghasilkan simbol dolar literal dan bukannya penggantian.
Kemudian, jalankan servis tersebut.
docker compose run --rm demoprintenv ALPINE_TAG keluar dengan status 1 dan tidak mencetak apa-apa. Pemboleh ubah tersebut tidak wujud di dalam container. Itu adalah salah faham yang paling biasa: .env mengkonfigurasi fail compose, bukan proses tersebut. Fail .env yang mengandungi POSTGRES_PASSWORD=hunter2 tidak melakukan apa-apa untuk pangkalan data anda melainkan sebahagian daripada fail compose merujuk kepadanya.
${NAME:-default} membekalkan nilai sandaran (fallback) apabila pemboleh ubah tidak ditetapkan atau kosong. ${NAME:?message} menyebabkan Compose enggan bermula dan mencetak mesej anda, yang merupakan pilihan tepat untuk nilai yang tidak mempunyai nilai lalai yang selamat.
env_file memuatkan pemboleh ubah ke dalam kontena
Atribut env_file: menamakan satu atau lebih fail yang kandungannya menjadi pemboleh ubah persekitaran kontena.
printf 'GREETING=from_env_file\nAPP_MODE=production\n' > app.envservices:
demo:
image: alpine:3.20
command: printenv GREETING
env_file:
- ./app.envdocker compose run --rm demoIni akan mencetak from_env_file. Format fail adalah baris KEY=value biasa, satu bagi setiap baris, dengan # memulakan ulasan. Ia bukan shell, walaupun Compose menghuraikannya dengan beberapa peraturan seperti shell. Tanda petikan di sekeliling akan dibuang, jadi KEY="value" dan KEY=value memberikan hasil yang sama. Nilai yang menggunakan tanda petikan berganda dan tanpa tanda petikan akan mempunyai placeholder ${...} yang diinterpolasi, manakala nilai yang menggunakan tanda petikan tunggal digunakan secara literal. Awalan export diterima tetapi tidak diperlukan. Ruang di sekeliling tanda = akan dipotong. Perkara yang perlu diberi perhatian ialah ulasan dalam baris: dalam nilai tanpa tanda petikan, ruang yang diikuti oleh # akan memulakan ulasan, jadi gunakan tanda petikan untuk sebarang nilai yang mengandungi #.
Laluan env_file yang tiada akan menyebabkan ralat dan Compose akan berhenti. Tandakannya sebagai pilihan jika fail tersebut mungkin tiada secara sah:
env_file:
- path: ./app.env
required: falseenvironment menetapkan pemboleh ubah secara inline
services:
demo:
image: alpine:3.20
command: printenv GREETING
environment:
GREETING: from_environmentDua sintaks diterima, iaitu bentuk pemetaan di atas dan bentuk senarai menggunakan - GREETING=from_environment. Kedua-duanya berkelakuan sama. Kedua-duanya berkongsi satu helah tambahan: kunci tanpa nilai akan melalukan pemboleh ubah daripada shell tempat anda menjalankan docker compose. Dalam bentuk senarai, ini adalah - GREETING kosong, dan dalam bentuk pemetaan, ini adalah GREETING: tanpa apa-apa selepas titik bertindih.
environment:
- GREETINGGREETING=from_my_shell docker compose run --rm demoIni mencetak from_my_shell. Jalankan arahan tersebut tanpa menetapkan GREETING dalam shell dan Compose tidak menetapkan apa-apa, tanpa sebarang amaran. Kegagalan laluan senyap perlu diketahui, kerana servis yang bermula dengan pemboleh ubah kata laluan kosong sering bermula dengan jayanya dan dibiarkan terbuka luas.
Yang mana satu diutamakan
Docker mendokumentasikan susunan keutamaan, bermula daripada yang tertinggi: docker compose run -e pada baris perintah, diikuti oleh environment atau env_file yang nilainya diambil daripada shell anda atau fail env, kemudian environment biasa dalam fail compose, seterusnya env_file, dan akhirnya arahan ENV yang terbina dalam imej.
Versi ringkas untuk kerja harian: environment: mengatasi env_file:, dan -e pada baris perintah mengatasi kedua-duanya. Buktikan perkara ini dalam satu fail.
services:
demo:
image: alpine:3.20
command: printenv GREETING
env_file:
- ./app.env
environment:
GREETING: from_environmentdocker compose run --rm demo
docker compose run --rm -e GREETING=from_cli demo printenv GREETINGYang pertama mencetak from_environment, jadi environment: telah mengatasi nilai dalam app.env. Yang kedua mencetak from_cli. Tiada apa-apa dalam fail compose yang mengatasi baris perintah.
Apabila kontena berkelakuan seolah-olah konfigurasi anda tidak digunakan, jangan meneka. docker compose config mencetak fail yang telah diselesaikan sepenuhnya, dan docker compose config --environment mencetak pemboleh ubah interpolasi yang digunakan oleh Compose. Kebanyakan laporan "fail env saya diabaikan" sebenarnya berpunca daripada nilai yang ditetapkan dua kali pada dua tahap yang berbeza.
Mengapa pemboleh ubah persekitaran bocor
Tetapkan kata laluan dalam environment: dan ia akan disimpan dalam konfigurasi kontena pada cakera, yang boleh dilihat oleh mana-mana pengguna dalam kumpulan docker.
docker compose run -d --name leaky -e DB_PASSWORD=hunter2 demo sleep 300
docker inspect leaky --format '{{json .Config.Env}}'Output tersebut mengandungi "DB_PASSWORD=hunter2" dalam teks biasa. Tiga lagi laluan mendedahkan nilai yang sama. docker compose config mencetaknya ke terminal, yang merupakan punca ia sering disalin dan ditampal ke dalam forum sokongan. Proses anak mewarisi pemboleh ubah secara lalai, melainkan proses induk sengaja memulakannya dengan persekitaran gantian, dan proses yang berjalan sebagai pengguna yang sama dengan PID 1, atau memegang CAP_SYS_PTRACE, boleh membacanya daripada /proc/1/environ. Dalam kontena di mana segala-galanya berjalan sebagai root, ini merangkumi setiap proses. Aplikasi yang berjalan sebagai pengguna tanpa keistimewaan manakala PID 1 berjalan sebagai root akan mendapat ralat kebenaran pada fail tersebut, walaupun ia masih memegang pemboleh ubah itu dalam persekitarannya sendiri. Selain itu, pengendali ranap aplikasi biasanya membuang keseluruhan persekitaran ke dalam log atau laporan ralat.
Keahlian dalam kumpulan docker secara efektif adalah root pada hos, jadi ini bukanlah sempadan keistimewaan yang boleh anda harapkan. Panduan mengenai akaun pengguna dengan keistimewaan paling rendah pada VPS menjelaskan mengapa kumpulan itu perlu dihadkan pada mana-mana kotak kongsi.
Compose secrets menyimpan nilai dalam fail
Compose menyokong rahsia berasaskan fail. Nilai tersebut dimount ke dalam kontena sebagai fail dan bukannya disuntik ke dalam persekitaran.
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./db_password.txtRahsia tersebut dimount pada /run/secrets/db_password di dalam kontena. Nama selepas garis miring ialah nama rahsia daripada blok peringkat atas secrets:.
Akhiran _FILE ialah konvensyen yang digunakan oleh Imej Rasmi Docker, termasuk postgres, mysql dan mariadb. Skrip entrypoint tersebut menyemak VARNAME_FILE, membaca fail itu, dan menggunakan kandungannya. Ini bukan ciri Docker, jadi ia hanya berfungsi jika imej tersebut melaksanakannya. Semak dokumentasi imej sebelum mengandaikan SOMETHING_FILE akan dipatuhi. Aplikasi yang tidak menyokongnya sering kali boleh membaca fail itu sendiri semasa permulaan, atau anda boleh menghantar laluan tersebut dan membiarkan entrypoint anda sendiri melakukannya.
Sahkan dari dalam kontena yang sedang berjalan:
docker compose exec db cat /run/secrets/db_password
docker compose exec db printenv POSTGRES_PASSWORDPerintah pertama mencetak kata laluan. Perintah kedua tidak mencetak apa-apa, kerana nilai tersebut tidak pernah dimasukkan ke dalam persekitaran. Itulah tujuan utamanya: docker inspect pada kontena ini hanya menunjukkan laluan yang tidak berbahaya.
Lindungi fail sumber pada hos, kerana kerahsiaan rahsia tersebut bergantung pada fail di sebaliknya:
chmod 600 db_password.txtJalan tengah yang pragmatik pada VPS
Banyak imej yang dihoskan sendiri tidak menyokong pemboleh ubah _FILE, jadi pemboleh ubah persekitaran (environment variables) adalah satu-satunya cara untuk memasukkannya. Pada VPS dengan pentadbir tunggal, matlamat yang realistik adalah untuk menghalang nilai tersebut daripada berada dalam fail yang boleh dibaca oleh semua orang dalam direktori projek anda, dan untuk menyimpannya di luar git.
sudo install -o root -g root -m 600 /dev/null /etc/myapp/app.env
sudo nano /etc/myapp/app.env env_file:
- /etc/myapp/app.envinstall -m 600 mencipta fail dengan mod yang telah ditetapkan, jadi tiada tempoh masa di mana fail tersebut boleh dibaca oleh sesiapa sahaja. Root memilikinya, jadi pengguna bukan root pada pelayan tidak boleh membacanya, walaupun sesiapa yang boleh menjalankan docker masih boleh membaca nilai tersebut daripada kontena. Tambahkan *.env dan .env ke dalam .gitignore dan lakukan commit pada app.env.example yang mengandungi nama kunci dengan nilai kosong sebagai gantinya. Kata laluan yang telah di-commit adalah kata laluan yang perlu ditukar (rotated).
Menukar nilai bermaksud memulakan semula servis. Pemboleh ubah persekitaran dibaca sekali sahaja apabila proses kontena bermula, jadi menyunting fail tidak mengubah apa-apa sehingga anda menjalankan docker compose up -d --force-recreate db. Ini adalah corak yang sama digunakan dalam panduan n8n di sebalik HTTPS pada VPS, di mana kunci penyulitan disimpan di luar fail compose.
Memisahkan konfigurasi mengikut persekitaran
Compose membaca .env daripada direktori projek secara lalai. Halakan ia ke lokasi lain menggunakan --env-file.
docker compose --env-file .env.staging configBerbilang fail dibaca mengikut urutan, dan fail yang dibaca kemudian akan mengatasi fail sebelumnya. Simpan tetapan lalai yang bukan rahsia dalam fail yang dikomitkan, dan simpan rahsia dalam fail yang tidak pernah meninggalkan pelayan. Perkara yang sama terpakai untuk env_file:, di mana fail terakhir yang disenaraikan akan diguna pakai bagi kunci yang bertindih.
FAQ
Mengapa fail .env saya diabaikan di dalam container?
Fail tersebut tidak diabaikan. Fail .env menggantikan placeholder ${NAME} dalam fail compose, dan ia juga boleh menetapkan pemboleh ubah Compose sendiri seperti COMPOSE_PROJECT_NAME atau COMPOSE_FILE. Ia tidak pernah menetapkan pemboleh ubah di dalam container. Untuk memasukkan nilai tersebut ke dalam container, buat rujukan kepadanya: environment: { KEY: "${NAME}" }, atau gunakan env_file: ./that-file.env sebagai ganti.
Adakah environment mengatasi env_file, atau sebaliknya?
environment: akan menang. Susunan yang didokumentasikan oleh Docker meletakkan atribut environment di atas atribut env_file, dan kedua-duanya berada di bawah docker compose run -e pada baris perintah. Jika sesuatu kunci ditetapkan di kedua-dua tempat, nilai dalam env_file akan diabaikan secara senyap.
Bagaimanakah saya boleh melihat nilai akhir yang akan digunakan oleh Compose?
Jalankan docker compose config untuk mencetak fail compose yang telah diselesaikan sepenuhnya dengan semua interpolasi digunakan. Bagi container yang sedang berjalan, docker inspect <container> --format '{{json .Config.Env}}' menunjukkan dengan tepat apa yang diterima oleh prosesnya.
Adakah secret Compose disulitkan?
Tidak. Secret berasaskan fail dipasang ke dalam container sebagai fail biasa pada /run/secrets/<name>, dan fail sumber berada pada cakera hos tanpa disulitkan. Kelebihannya adalah dari segi skop, bukan penyulitan: nilai tersebut kekal di luar persekitaran container, di luar output docker inspect, dan di luar dump ranap yang mencetak persekitaran.
Bolehkah saya menggunakan tanda petikan dan ruang dalam fail env?
Ya. Compose membuang tanda petikan di sekelilingnya, jadi KEY="value with spaces" dan KEY=value with spaces kedua-duanya memberikan value with spaces. Gunakan tanda petikan tunggal apabila nilai mengandungi $ yang tidak boleh diinterpolasikan, dan letakkan tanda petikan pada sebarang nilai yang mengandungi #, kerana jika tidak diletakkan tanda petikan, ia akan dianggap sebagai permulaan ulasan. Ruang di sekeliling = akan dibuang, jadi KEY = value masih menetapkan KEY.