6 คะแนน โดย GN⁺ 2025-10-15 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • แม้ซอฟต์แวร์จะพัฒนาไปอย่างรวดเร็ว แต่ ระบบตัวแปรสภาพแวดล้อมของระบบปฏิบัติการ ยังคงโครงสร้างเดิมจากเมื่อหลายสิบปีก่อน
  • ตัวแปรสภาพแวดล้อมมีลักษณะเป็น พจนานุกรมสตริงแบบโกลบอล ที่เรียบง่าย ไม่มี namespace หรือ type
  • บน Linux ตัวแปรสภาพแวดล้อมจะถูก ส่งต่อจากโปรเซสแม่ไปยังโปรเซสลูก ผ่าน system call execve
  • Bash, glibc, Python และระบบอื่น ๆ ต่างจัดการตัวแปรสภาพแวดล้อมในรูปแบบ hash map, array และ dictionary wrapper ตามแบบของตน
  • มาตรฐาน POSIX ไม่ได้บังคับให้ชื่อต้องเป็นตัวพิมพ์ใหญ่เท่านั้น และในทางปฏิบัติก็มีความยืดหยุ่น เช่น แนะนำให้ใช้ชื่อตัวพิมพ์เล็ก ในบางกรณี

ตัวแปรสภาพแวดล้อมคืออะไร

  • แม้ภาษาโปรแกรมจะพัฒนาอย่างรวดเร็ว แต่โครงสร้างพื้นฐานสำหรับการรันโปรเซสที่ระบบปฏิบัติการมอบให้ โดยเฉพาะส่วนของ ตัวแปรสภาพแวดล้อม แทบไม่เปลี่ยนแปลงเลย
  • เมื่อต้องส่งพารามิเตอร์ขณะรันให้แอปพลิเคชันโดยไม่ใช้ไฟล์แยกหรือ IPC ในความเป็นจริงก็มักเลี่ยงไม่ได้ที่จะต้องใช้อินเทอร์เฟซที่อิงกับตัวแปรสภาพแวดล้อม
  • ตัวแปรสภาพแวดล้อมทำหน้าที่เป็น พจนานุกรมสตริงแบบแบน ไม่มี namespace และไม่มี type

โครงสร้างการสร้างและการส่งต่อของตัวแปรสภาพแวดล้อม

  • ตัวแปรสภาพแวดล้อมเป็น วิธีดั้งเดิมในการส่งค่า ระหว่างโปรเซส โดยโปรเซสแม่จะส่งต่อไปพร้อมกันตอนรันโปรเซสลูก
    • กล่าวคือเป็นโครงสร้างที่ สืบทอด จากโปรเซสแม่ไปยังโปรเซสลูก
  • บน Linux system call execve รับไฟล์ executable, อาร์กิวเมนต์ และอาร์เรย์ของตัวแปรสภาพแวดล้อม (envp) เป็นพารามิเตอร์
    • ตัวอย่างคำสั่งรัน: ls -lah
      • filename: /usr/bin/ls
      • argv: ['ls', '-lah']
      • envp: ['PATH=...','USER=...']
  • โปรเซสแม่สามารถส่งต่อสภาพแวดล้อมเดิมให้โปรเซสลูกได้ตรง ๆ หรือจะ ประกอบสภาพแวดล้อมใหม่ทั้งหมด ก็ได้
    • เครื่องมือเกือบทั้งหมด (Bash, subprocess.run ของ Python, execl ในไลบรารี C เป็นต้น) จะส่งต่อตัวแปรสภาพแวดล้อมตามเดิม
    • แต่บางเครื่องมือ เช่น login จะสร้าง สภาพแวดล้อมใหม่

ตำแหน่งจัดเก็บและการประมวลผลภายในของตัวแปรสภาพแวดล้อม

  • เคอร์เนลจะเก็บตัวแปรสภาพแวดล้อมไว้บน สแตกในรูปแบบสตริงที่ลงท้ายด้วย null ตอนเริ่มโปรแกรม
  • ข้อมูลนี้โปรแกรมแก้ไขโดยตรงได้ยาก และโดยทั่วไปจะถูกคัดลอกเข้าไปจัดการในโครงสร้างภายในของโปรแกรมเอง
  • วิธีจัดเก็บตัวแปรสภาพแวดล้อมของแต่ละภาษาและเชลล์
    • Bash: จัดการด้วย hash map (dictionary) แบบโครงสร้างสแตก
      • ทุกครั้งที่เรียกฟังก์ชันจะสร้างแมปของ local scope
      • มีเพียงตัวแปรที่ export แล้วเท่านั้นที่ถูกส่งต่อไปยังโปรเซสลูก
      • ตัวแปรที่ประกาศด้วย local ก็สามารถส่งต่อไปยังโปรเซสลูกผ่าน export ได้
        • ตัวอย่าง: ใช้ export PATH เพื่อสะท้อนการเปลี่ยนแปลงแบบ local ไปยังโปรเซสลูก โดยไม่กระทบกับระดับโกลบอล
    • glibc (ไลบรารี C): จัดการ environ ที่เป็นโครงสร้าง dynamic array ผ่าน putenv, getenv
      • เพราะเป็นโครงสร้างอาร์เรย์ ทั้งการค้นหาและการแก้ไขจึงมี ความซับซ้อนเชิงเส้นเวลา
      • ดังนั้นจึงไม่เหมาะสำหรับใช้เก็บข้อมูลที่ต้องการประสิทธิภาพสูง
    • Python: ภายในเปิดให้ใช้งานเหมือน dictionary ผ่าน os.environ แต่แท้จริงแล้ว เชื่อมโยง กับอาร์เรย์ environ ของไลบรารี C
      • เมื่อเปลี่ยนค่าใน os.environ จะมีการเรียก os.putenv เพื่อสะท้อนผลไปยังไลบรารี C ด้วย
      • แต่ในทิศทางกลับกันจะไม่ซิงก์ จึงมีลักษณะเป็น ทางเดียว

ฟอร์แมตและขอบเขตที่ยอมรับได้ของตัวแปรสภาพแวดล้อม

  • Linux kernel และ glibc ใจกว้างมากกับฟอร์แมตของตัวแปรสภาพแวดล้อม
    • ชื่อเดียวกันสามารถซ้ำกันและมีหลายค่าได้
    • ลงทะเบียนได้แม้ไม่มี = และไม่มีข้อจำกัดพิเศษกับอักขระอย่างอีโมจิ
  • ข้อจำกัดด้านขนาดที่ใช้งานได้
    • ตัวแปรเดี่ยว: 128 KiB (โดยทั่วไปในสภาพแวดล้อม x64)
    • ผลรวมทั้งหมด: 2 MiB (ใช้ร่วมกับอาร์กิวเมนต์บรรทัดคำสั่ง)
    • ตัวแปรสภาพแวดล้อมถูกจำกัดไม่ให้ เกิน 1/4 ของพื้นที่สแตก

จุดแปลกและ edge case ของตัวแปรสภาพแวดล้อม

  • Bash จะ ลบชื่อที่ซ้ำกันและเพิกเฉยต่อรายการที่ผิดปกติ เมื่อเจอตัวแปรสภาพแวดล้อมแปลก ๆ (เช่น ชื่อซ้ำ, รายการที่ไม่มี = เป็นต้น)
  • หากชื่อตัวแปรมีช่องว่าง Bash จะไม่สามารถอ้างอิงชื่อนั้นได้ แต่ยังคงส่งต่อไปยังโปรเซสลูกได้
    • ตัวอย่างเช่น Nushell, Python เป็นต้น สามารถสร้างตัวแปรที่มีช่องว่างในชื่อได้
    • Bash จะเก็บรายการเหล่านี้ไว้ใน hash map แยกต่างหาก (invalid_env) เพื่อจัดการ

กฎฟอร์แมตและการตั้งชื่อของตัวแปรสภาพแวดล้อมตามมาตรฐาน

  • มาตรฐาน POSIX ถือว่าเป็นตัวแปรได้ตราบใดที่ชื่อไม่มีเครื่องหมายเท่ากับ (=)
    • คำแนะนำอย่างเป็นทางการ: ชื่อควรมีได้เฉพาะตัวพิมพ์ใหญ่ ตัวเลข และขีดล่างเท่านั้น (ตัวแรกห้ามเป็นตัวเลข)
    • ตัวแปรตัวพิมพ์เล็กมีไว้สำหรับ namespace เฉพาะของแอปพลิเคชัน
    • เครื่องมือมาตรฐานใช้ตัวพิมพ์ใหญ่เท่านั้น แต่ก็ อนุญาตให้ใช้ตัวแปรตัวพิมพ์เล็ก ได้
  • ในการใช้งานจริง นักพัฒนามักตั้งชื่อแบบ ALL_UPPERCASE
  • กฎที่แนะนำ: ใช้ regex ^[A-Z_][A-Z0-9_]*$ สำหรับชื่อตัวแปร และใช้ UTF-8 สำหรับค่า
    • หากกังวลเรื่องข้อยกเว้นหรือความเข้ากันได้ แนะนำให้ใช้ Portable Character Set (ASCII) ของ POSIX

บทสรุป

  • ตัวแปรสภาพแวดล้อมยังคงเป็น อินเทอร์เฟซที่ล้าสมัยแต่จำเป็น และทำหน้าที่เป็นรอยต่อระหว่างระบบปฏิบัติการกับแอปพลิเคชัน
  • แม้จะมีข้อจำกัดเชิงโครงสร้าง Bash, C, Python และระบบอื่น ๆ ก็ยังคงห่อหุ้มและใช้งานมันในแบบของตัวเอง
  • ในระบบสมัยใหม่ ความต้องการวิธีจัดการการตั้งค่าที่มี namespace ชัดเจนและระบบ type ที่ชัดกว่า กำลังเพิ่มขึ้นเรื่อย ๆ

2 ความคิดเห็น

 
howudoin 2025-10-15

แม้ดูเผินๆ เหมือนความสำคัญจะลดลงไปบ้าง แต่การมาของ Docker และคลาวด์ก็ทำให้มันกลายเป็นสิ่งที่หลีกเลี่ยงไม่ได้อีกครั้ง

 
GN⁺ 2025-10-15
ความคิดเห็นจาก Hacker News
  • ผมทำงานสาย SRE/Sysadmin/DevOps/อะไรก็ตาม บนบล็อกผมอาจพูดเรื่องการทำมาตรฐาน environment variables แบบเบา ๆ แต่ก็อยากชี้ให้เห็นว่าทางเลือกอื่น ๆ ก็ชวนอึดอัดพอกัน โดยเฉพาะเมื่อมีข้อมูลลับ (secrets) เข้ามาเกี่ยวข้อง
    โครงสร้างที่ให้แอปพลิเคชันเข้าถึงคลังเก็บข้อมูลลับ (vault) โดยตรง เช่น Hashicorp Vault/OpenBao/Secrets Manager มักนำไปสู่ vendor lock-in อย่างรุนแรงอย่างรวดเร็ว และตอนจะเปลี่ยนก็ลามลงไปถึงระดับไลบรารี ทำให้ยากมาก
    ความพร้อมใช้งานของ Vault จะกลายเป็นเรื่องสำคัญมาก และเมื่อถึงเวลาต้องอัปเกรดหรือบำรุงรักษา ทีมปฏิบัติการก็ลำบากอย่างหนัก
    ถ้าส่งข้อมูลลับผ่านไฟล์ config ก็จะติดปัญหาว่าจะบรรจุความลับนั้นอย่างไร เพราะไฟล์ config มักอยู่ในเส้นทางที่เปิดกว้าง
    สุดท้ายก็มักต้องพึ่งไม่ทางใดก็ทางหนึ่ง ระหว่าง "ให้ระบบที่มีสิทธิพิเศษแทนค่าลง template ก่อนส่งให้แอป" หรือ "เก็บไฟล์ config ทั้งไฟล์ไว้ในคลังเก็บความลับแล้วส่งให้แอป"
    งานทำ template ก็ผิดพลาดได้ง่าย ส่วนการย้ายไฟล์ config ทั้งชุดไปไว้ในคลังเก็บความลับก็เสี่ยงที่ใครสักคนจะอัปโหลดผิด จนสร้างความเครียด
    ทุกวันนี้ระบบส่วนใหญ่รันอยู่บนคอนเทนเนอร์ และถ้าไม่ใช่บริษัทที่ดูแลโครงสร้างพื้นฐานอย่างดี ไฟล์ config ก็มักจะไปอยู่ผิดที่ผิดทาง ทำให้ขั้นตอน mount ยิ่งสับสนและเกิดความผิดพลาดได้บ่อย
    ไม่ว่าจะใช้ฟอร์แมตอะไรอย่าง JSON/YAML/TOML ก็มีบั๊กประหลาดเฉพาะตัวเป็นเรื่องปกติ เช่นปัญหา Norway ของ YAML
    ผมเคยเห็นวิธีรับความลับผ่าน Kubernetes Secrets API แต่วิธีนี้ก็ยังเจอปัญหา vendor lock-in หนักเหมือนกัน
    ถ้าไม่ได้ออกแบบระบบอย่าง operator โดยเฉพาะ ผมไม่ค่อยแนะนำวิธีนี้เชิงรุก
    ผมก็เคยเห็นปัญหาที่เกิดจากการตั้ง environment variables ผ่าน subprocess เหมือนกัน แต่คิดว่าทีมสมัยนี้มักใช้ระบบที่อิง message bus มากกว่า ซึ่งแข็งแรงกว่าและขยายแยกส่วนได้ดีกว่า

    • ทีมของเราเคยทำไลบรารี Secrets แบบเบา ๆ ที่ใช้งานทั่วไปได้ โดยให้ backend เฉพาะผู้ให้บริการอย่าง AWS Secrets Manager เสียบเป็นปลั๊กอินอยู่ด้านหลัง
      มันตั้งค่า local cache ได้และมีตัวเลือกข้าม cache รายพารามิเตอร์ได้ด้วย เลยทำให้ตรรกะที่ผูกกับผู้ให้บริการจริง ๆ อยู่แค่ใน backend ส่วนไลบรารีกับแอปพลิเคชันยังไม่ผูกกับผู้ให้บริการ
      ตอนย้ายไป Vault ก็แค่เพิ่ม backend อีกตัวแล้วเปลี่ยน config ก็ใช้งานได้แทบไม่มีปัญหา

    • อยากรู้ว่าทำไม Kubernetes secret API ถึงถูกมองว่าเป็นปัญหา vendor lock-in
      หรือว่าเคยพยายามใช้ deployment yaml เพื่อวัตถุประสงค์อื่นที่ไม่ใช่การ deploy บน Kubernetes?
      สำหรับแอปส่วนใหญ่ คุณสามารถ mount secret เข้าไปในคอนเทนเนอร์ แล้ว inject ให้แอปเป็น environment variables หรือไฟล์ json เพื่อให้อ่านเขียนได้อย่างอิสระจากสภาพแวดล้อม
      เท่าที่ผมเข้าใจ การเข้ารหัส backend ของ etcd ก็ทำผ่าน KMS ได้เช่นกัน

    • ผมยังไม่ค่อยเข้าใจว่าการรับความลับผ่าน Kubernetes Secrets API ทำไมถึงนับเป็น lock-in
      โดยพื้นฐานแล้ว K8s secrets ไม่ได้ถูกเก็บแบบเข้ารหัส ดังนั้นผมมองว่าจะมีความหมายก็ต่อเมื่อ (0) ใช้ K8s อยู่แล้ว (1) ตั้งค่าการเข้ารหัสไว้ที่ control plane และ (2) ใช้โซลูชันเสริมอย่าง CSI driver ด้วย
      และ Secret Store CSI Driver ก็รองรับ backend หลากหลายอย่าง Conjur ด้วย ดังนั้นจริง ๆ แล้วเหมือนจะตรงข้ามกับ lock-in มากกว่า

    • ด้วยเหตุนี้ผมเลยยังใช้ config แบบ env vars กับ dotenv เป็นหลัก
      โครงสร้าง config ที่อิง environment variables นั้นเรียบง่ายมาก และเข้ากันได้ดีกับเครื่องมือต่าง ๆ เช่น secret managers
      ช่วงไม่กี่ปีมานี้ผมเริ่มสนใจ sOps ที่อิง YAML ทีละนิด
      YAML นั้นตรงไปตรงมามากในการแสดงโครงสร้าง config ของแอป และใช้ sops เข้ารหัสแค่บางส่วนเพื่อจัดการก็ง่าย
      แม้ว่าการจัดการกุญแจ GPG จะยุ่งยากอยู่บ้าง แต่ก็แก้ได้ด้วย Vault หรือ OpenBao
      เพียงแต่ว่าระหว่างทางก็จะกลับไปเจอปัญหา vendor lock-in อีก โดย OpenBao ดูจะเบากว่านิดหน่อย

    • คุณสามารถรับ environment variables จากผลลัพธ์ของการรันคำสั่งได้ด้วย จึงจัดการเรื่องนี้ได้โดยไม่ต้องผ่านขั้นตอน template และไม่ติด vendor lock-in

  • เกร็ดที่น่าสนใจอีกอย่างคือ setenv() นั้นพังในเชิงพื้นฐานบน POSIX ดังนั้นผมคิดว่าไม่ควรใช้ในโค้ดไลบรารีเด็ดขาด
    แม้แต่ในโค้ดแอปเองก็ควรใช้เป็นทางเลือกสุดท้าย และต้องใช้ก่อนสร้าง thread เท่านั้น
    getenv() จะคืน pointer ไปยังค่า environment variable ต้นฉบับโดยตรง ดังนั้นเวลาที่ setenv() เขียนทับตัวแปร จึงไม่มีตัวป้องกันใด ๆ
    ต้องระวังมาก

    • ถ้าจะตั้ง environment variables ให้ถูกต้อง ผมคิดว่าควรตั้งผ่าน execve()
      วิธีนี้เหมาะเมื่อจะส่งข้อมูลผ่าน environment variables เฉพาะช่วงก่อนและหลัง exec() เท่านั้น

    • ผมไม่เข้าใจว่าทำไมในโค้ดไลบรารีถึงต้องใช้ setenv

    • Solaris แก้ปัญหานี้ไปแล้ว แต่ Linux ยังยึดวิธีเดิมอยู่

    • NetBSD มีทางเลือกที่ปลอดภัยชื่อ getenv_r() มานานแล้ว และล่าสุด FreeBSD ก็เพิ่มเข้ามาแล้ว
      macOS ก็น่าจะตามมาในอีกไม่นาน
      ก่อนหน้านี้ก็เคยมีความพยายามใส่มันเข้าไปใน glibc หรือ POSIX แต่ถูกปฏิเสธ
      ถ้ามันแพร่หลายบนหลายแพลตฟอร์ม ผมหวังว่าสุดท้ายมันจะถูกยอมรับอย่างเป็นทางการ
      เอกสาร NetBSD getenv_r
      คอมมิตของ FreeBSD

  • environment variables มักถูกใช้ส่งข้อมูลลับ แต่ผมไม่คิดว่านั่นเป็นแนวปฏิบัติที่ดีนัก
    บน Linux ทุกโปรเซสที่รันด้วยผู้ใช้คนเดียวกันสามารถส่อง environment variables ของกันและกันได้
    ไม่ว่าจะตั้ง threat model แบบไหน โดยเฉพาะบนเครื่องนักพัฒนา มันก็น่ากังวลเพราะมีโปรเซสจำนวนมากรันด้วยผู้ใช้คนเดียวกัน
    ปัญหานี้หนักขึ้นอีกเมื่อมีหลายโปรเซสวิ่งนอกคอนเทนเนอร์ เช่น LLM agents
    และโดยทั่วไป environment variables จะถูกสืบทอดไปถึง child processes ด้วย ทำให้แม้จะมีเพียงโปรเซสเดียวที่ต้องใช้ข้อมูลลับ ก็ยังมีแนวโน้มจะถูกเปิดเผยอย่างกว้างเกินจำเป็น
    systemd แสดง environment variables ให้ system clients ทั้งหมดผ่าน DBUS และในเอกสารทางการก็เตือนอย่างชัดเจนว่าอย่าใส่ข้อมูลลับไว้ใน environment variables
    ถ้านี่เป็นเรื่องจริง ก็แปลว่า environment variables ที่ตั้งไว้ใน unit สำหรับ root เท่านั้น อาจถูกผู้ใช้ทั่วไปมองเห็นได้ด้วย ซึ่งผมคิดว่าน่าจะช็อกพอสมควรสำหรับผู้ดูแลระบบหลายคน
    สุดท้ายแล้วผมคิดว่ามีเพียงโครงสร้างที่ให้ secret manager ส่งความลับผ่านการแชร์ไฟล์ชั่วคราวเท่านั้น (เช่น op cli ของ 1Password, flask, terraform เป็นต้น) ที่ช่วยให้หลุดพ้นจากทั้ง environment variables และไฟล์ข้อความล้วนได้
    ระบบ credentials ของ systemd ก็เป็นแบบนี้ แต่การรองรับยังมีน้อย
    ถ้าใครรู้วิธีที่ดีในการส่งความลับโดยไม่ใช้ environment variables หรือไฟล์ข้อความล้วน ก็อยากให้ช่วยแชร์
    อย่างกรณี 1Password op client ผมรู้สึกว่าปลอดภัยเวลาจะใช้ใน CLI session เพราะทุก session ต้องได้รับการอนุมัติจากผมก่อน และแม้จะมีโปรเซสอันตรายเรียกใช้ไบนารี op ก็ยังต้องขออนุมัติแยกต่างหาก
    ปัญหาที่เหลือคือจะส่งความลับนั้นต่อไปยังโปรเซสที่ต้องใช้จริงอย่างไร ซึ่งก็เหมือนวนกลับมาที่จุดเริ่มต้นอีก
    ลิงก์เอกสารทางการของ systemd เรื่อง environment variables

    • ตั้งแต่ราวปี 2012 เป็นต้นมา environment variables ก็ปลอดภัยพอ ๆ กับหน่วยความจำทั่วไปแล้ว
      ประวัติคอมมิตที่เกี่ยวข้อง
      หากจะอ่าน environment variables ของโปรเซสอื่น ต้องมีสิทธิ ptrace ก่อน และถ้ามี ptrace อยู่แล้ว ก็อ่านข้อมูลลับทั้งหมดได้อยู่ดี ดังนั้นการกังวลเรื่องนี้จึงไม่มีความหมายตามข้อโต้แย้งนี้
      ข้อมูลบรรทัดคำสั่ง (cmdline) เป็นอีกเรื่องหนึ่ง แต่ environment variables ไม่ได้รั่วไหลได้ง่ายแบบเดิมอีกแล้ว

    • ในโมเดลความปลอดภัยของระบบปฏิบัติการส่วนใหญ่ การที่รันด้วยผู้ใช้คนเดียวกันก็แทบเท่ากับมอบสิทธิทั้งหมดของผู้ใช้นั้นไปทั้งหมด
      มีข้อยกเว้นเป็นฟีเจอร์ความปลอดภัยเสริมอย่าง capsicum ของ FreeBSD, landlock ของ Linux, SELinux, AppArmor, integrity label ของ Windows เป็นต้น แต่ส่วนใหญ่ก็มีข้อจำกัดชัดเจน
      สุดท้ายแล้วผมก็มีอิสระที่จะ kill, stop หรือ debug โปรเซสของตัวเองได้ และเข้าถึง secret ของโปรเซสที่ผมเป็นเจ้าของได้เสมอผ่าน ptrace/process_vm_readv/ReadProcessMemory และอื่น ๆ
      มันก็มีโมเดลความปลอดภัยแบบอื่นอยู่บ้าง เช่น OS ที่เป็น capability-based อย่างแท้จริง แต่ระบบส่วนใหญ่ยังยึดโมเดลนี้อยู่ จึงต้องเข้าใจข้อจำกัดและความรับผิดชอบของมัน

    • ถ้าต้องการวิธีส่งความลับที่ไม่ใช้ environment variables หรือไฟล์ข้อความล้วน ผมนึกถึง memfd_secret
      man page ของ memfd_secret
      การรองรับในแต่ละภาษาและเฟรมเวิร์กยังมีไม่มาก โดยเฉพาะใน Rust หรืออาจรวมถึง Go ถ้าทำได้ ก็อาจลองผ่าน FFI ดู
      ผมเคยคิดจะห่อมันไว้ใช้ฝั่ง PHP เอง แต่ไม่อยากไปแตะถึงขั้นแก้ php-fpm เลยหยุดไว้
      ในทางปฏิบัติ ถ้า process manager เปิด secret file descriptor ไว้ล่วงหน้าแล้วส่งต่อให้ child process ใช้ได้ โดยไม่ทำให้มันไปโผล่ในหน่วยความจำหรือที่อื่น ก็น่าจะปลอดภัยที่สุด

    • โมเดลความปลอดภัยแบบ Unix ดั้งเดิมยังถูกใช้อย่างแพร่หลายอยู่ แม้จะมีการปรับปรุงทีละเล็กทีละน้อย แต่สำหรับสภาพแวดล้อมคอมพิวเตอร์ราคาถูกหรือสภาพแวดล้อมสมัยใหม่ ข้อจำกัดก็ชัดเจนมาก
      ถ้าคุณต้องการซ่อนความลับจากโปรเซสอื่นจริง ๆ วิธีมาตรฐานก็คือแยกไปรันคนละผู้ใช้
      หรือไม่ก็ใช้วิธีเข้าถึงจากระยะไกลไปเลย ซึ่งแน่นอนว่าก็มีข้อเสียและความซับซ้อนของมัน

    • ปัจจุบันบนแพลตฟอร์มคอนเทนเนอร์ มักแนะนำให้ส่ง config หรือ secret ผ่าน environment variables
      ภายในคอนเทนเนอร์จะถูกออกแบบมาไม่ให้โปรเซสอื่นมาส่อง environment variables ได้
      การที่ environment variables ถูกสืบทอดไปยัง child process ก็เป็นเจตนาของการออกแบบ เพราะตัวที่จัดสภาพแวดล้อมและมี secret อยู่แล้วก็เป็นฝ่ายตั้งค่านั้นโดยตรง
      ปัญหาส่วนใหญ่ที่คุณกังวล ผมมองว่าไม่ได้ใหญ่ขนาดนั้น และถ้าจำเป็นก็ยินดีคุยเชิงรายละเอียด

  • หลายคอมเมนต์เน้นไปที่การจัดการความลับและปัญหาต่าง ๆ แต่ก็น่าลองคิดถึงข้อดีของ environment variables บ้าง
    environment variables คือ "การ bind ตัวแปรแบบ dynamic extent ที่มีขอบเขตไม่สิ้นสุด" ซึ่งเชื่อมกระบวนการของ Unix เข้าด้วยกันในเชิงโครงสร้าง
    แทนที่จะเอาไปเทียบกับไฟล์ข้อความธรรมดาตรง ๆ เราควรจำไว้ว่าจุดมีอยู่ของ environment variables คือการส่งต่อ context เพื่อให้ข้อมูลส่งผ่านไปยัง child process อย่างปลอดภัย
    ยิ่งโครงสร้างโปรเซสซับซ้อน เช่น shell ซ้อนกันหลายชั้น หรือ subprocess ของโปรแกรมใหญ่ บทบาทของ environment variables ก็ยิ่งเด่นชัด

  • อยากแนะนำ Varlock มาก เพราะมีประโยชน์จริง ๆ
    มันช่วยกำหนดได้ชัดเจนว่า environment variables ไหนจำเป็นหรือเป็นตัวเลือก เป็นชนิดข้อมูลอะไร และควรดึงมาจากไหน ทำให้จัดการได้ง่าย
    เว็บไซต์ทางการของ Varlock

  • จากประสบการณ์จริง ถ้าจะยกตัวอย่างว่า environment variables ซับซ้อนได้แค่ไหน ผมเคยหลงทางเต็ม ๆ ตอนพยายาม debug ว่า ENV ตัวหนึ่งในบริษัทเก่าถูกตั้งค่ามาจากที่ไหน
    ตอนแรกคิดว่าน่าจะมาจาก .bashrc หรือจากที่ง่าย ๆ จุดเดียว แต่จริง ๆ แล้วมันถูกตั้งค่าผ่านอย่างน้อย 10 ชั้น ตั้งแต่ระดับทั้งบริษัท ระดับภูมิภาค หน่วยธุรกิจ ทีม ไปจนถึงระดับบุคคล
    สุดท้ายต้องเปิด bash debug flags ถึงจะค่อย ๆ ไล่ได้ว่ามันถูกตั้งจากตรงไหนบ้าง

    • ไม่แน่ใจว่าภาษาอื่นรองรับไหม แต่ Node.js เพิ่งเพิ่ม command line flag ที่ช่วย trace การเข้าถึงและการเปลี่ยนแปลง environment variables ได้อย่างแม่นยำ
      เอกสาร Node.js --trace-env
      เพราะค่าพวกนี้อาจถูกตั้งหรือเปลี่ยนได้ผ่าน API มากมาย จึงน่าจะมีประโยชน์มากในการ debug ที่ซับซ้อน

    • เป็นเคสที่ทำให้นึกถึงคำว่า "แค่ namespace เดียวก็น่าจะพอหรือเปล่า"

  • ผมเลิกใช้ environment variables ไปนานแล้ว
    ตอนนี้ผมวางไฟล์ dmd.conf ไว้ข้าง ๆ คอมไพเลอร์ แล้วให้คอมไพเลอร์อ่านไฟล์นั้นโดยตรง

  • ปัญหาที่ร้ายแรงที่สุดของ environment variables คือความเป็นนัยและความไม่โปร่งใส
    ในโลก *nix แอปส่วนใหญ่มักพึ่งพา environment variables
    ถึงจะรองรับวิธีตั้งค่าที่ชัดเจนและโปร่งใสกว่าเพิ่มเติมด้วยก็ตาม เช่นไฟล์ตั้งค่า, remote service หรือ command line arguments การรองรับ environment variables ก็ยังเป็นธรรมเนียมของวงการนี้
    สุดท้าย environment variables ก็เป็น global hash map เช่นกัน แค่ถูก clone และขยายเพื่อส่งต่อไปยัง child process ซึ่งอาจเป็นดีไซน์ที่สมเหตุสมผลในปี 1979 แต่ทุกวันนี้กลับกลายเป็นพิษบ่อยครั้ง
    ตัวอย่างเช่น Kubernetes จะทำให้ environment ของคอนเทนเนอร์ปนเปื้อนด้วย environment variables แบบ "service link" โดยปริยาย
    ถ้า environment variables ที่แอปคาดไว้ชนกับ default env var การ debug จะยากมาก
    เอกสารทางการของ Kubernetes
    ยิ่งไปกว่านั้น ผมรู้สึกว่ายังมีพฤติกรรมอีกมากที่สืบทอดกรอบเดิม ๆ มาแบบไม่ตั้งคำถาม เช่น /bin, /usr/bin, /lib, /usr/lib
    อ้างอิง: Ubuntu Q&A เรื่องการคงไดเรกทอรีแบบ legacy

    • ปุ่มอย่าง hjkl ก็อาจนับเป็นอีกตัวอย่างของความล้าสมัยแบบนี้ได้
      hjkl ใน vi มีที่มาจาก dumb terminal เมื่อ 40 ปีก่อน ซึ่งเป็นเทอร์มินัลที่ขายได้น้อยมาก
      (น้อยกว่า Nokia N9 เสียอีก)
  • ทุกครั้งที่ผมตั้ง environment variables บน Linux จะรู้สึกไม่สบายใจ
    วิธีที่ถือว่าเป็นทางการก็ดูต่างกันไปตามแต่ละดิสทริบิวชัน และถึงจะทำตามคู่มือออนไลน์ พอรีบูตหรือปิดเทอร์มินัลก็หายหมด
    ผมอยากให้มี GUI editor สำหรับ environment variables แบบง่าย ๆ ที่ใช้ได้ทั้งระบบเหมือนบน Windows
    ของ Windows แม้จะมีความยุ่งยากตรงที่ต้องเปิดเทอร์มินัลใหม่เพื่อให้ค่ามีผล แต่ที่เหลือมันก็ทำงานได้ดีเสมอ

    • environment variables ไม่ได้คงอยู่ข้าม session เป็นเรื่องปกติอยู่แล้ว ดังนั้นจึงต้องใส่ไว้ในที่ที่จะถูกรันใหม่ทุก session เช่นตอน login หรือเปิดเทอร์มินัล
      ตอน login จะรัน .bash_profile ส่วน child session จะรัน .bashrc
      ถ้าให้ .bash_profile ไป source .bashrc และเก็บการตั้งค่าส่วนใหญ่ไว้ใน .bashrc ก็จะจัดการง่าย
      ถ้าไม่ได้ใช้ Bash แต่ใช้ shell อื่นอย่าง zsh/fish ก็ต้องตั้งค่าตาม shell นั้น
      บน Linux ไม่มี GUI ทางการที่รวมศูนย์สำหรับ environment variables ซึ่งใช้ได้กับทุกเทอร์มินัล
      จะทำ GUI ที่ต้อง parse ความซับซ้อนเหล่านี้ก็ได้ แต่แก้ด้วย text editor ธรรมดากลับง่ายกว่า

    • ในฐานะคนที่ใช้ Linux เป็นหลัก ผมกลับรู้สึกว่าพฤติกรรมแบบ Windows น่ารำคาญกว่า
      มีแอปมากเกินไปที่ทำให้ environment variables ปนเปื้อน พออะไรพังขึ้นมาก็เลยงงกันบ่อย ๆ ว่าแท้จริงแล้ว $SOFTWARE ไปรันจากโฟลเดอร์ประหลาดที่ไหนกันแน่

    • ถ้าใช้ systemd คุณก็สามารถเขียน KEY=VALUE ลงใน /etc/environment หรือ /etc/environment.d/ ได้
      ที่จริงวิธีนี้ก็ดูเหมือนจะทำ GUI รองรับได้
      แต่ environment variables ก็มีข้อจำกัดตรงที่ไม่สามารถ inject เข้าไปในโปรเซสที่กำลังรันอยู่ได้ ต้องรีสตาร์ตเท่านั้นจึงจะมีผล
      อ้างอิงเอกสารทางการของ systemd

    • การ์ตูน xkcd Standards
      มันเล่าสถานการณ์ได้ขำดีว่า Linux มีวิธีตั้ง environment variables แข่งกันอยู่แล้วถึง 14 แบบ แล้วถ้าจะ "รวมให้เหลือแบบเดียว" วันถัดไปก็จะกลายเป็นมีมาตรฐาน 15 แบบแทน

  • เรื่องจุกจิกเกี่ยวกับ environment variables ที่ผมชอบที่สุดคือ PS1 และตัวคล้าย ๆ กันที่ทุกคนคิดว่าเป็น environment variables ทั้งที่จริงแล้วมันเป็น shell variables
    คุณมองไม่เห็น PS1 ผ่านคำสั่ง "env" ด้วยซ้ำ