ซอฟต์แวร์คอนเทนเนอร์กำลังบรรจุลงในหน่วยมาตรฐานเพื่อความสะดวกในการพัฒนาและปรับใช้ คอนเทนเนอร์รวมโค้ดจากแอปพลิเคชันของคุณเข้าด้วยกันพร้อมกับการขึ้นต่อกันทั้งหมด คอนเทนเนอร์สามารถยืนอยู่คนเดียวได้ทั้งหมด ประกอบด้วยแพ็คเกจที่มีซอฟต์แวร์ของคุณ สภาพแวดล้อมรันไทม์ และไลบรารีระบบ คอนเทนเนอร์ช่วยให้นักพัฒนาและทีมปฏิบัติการมั่นใจได้ว่าซอฟต์แวร์จะทำงานเหมือนกันโดยไม่คำนึงถึงสภาพแวดล้อม การแยกโค้ดออกจากโครงสร้างพื้นฐาน ทำให้แอปที่ "มีคอนเทนเนอร์" ทำงานแบบเดียวกันในสภาพแวดล้อมภายใน สภาพแวดล้อมการทดสอบ และการใช้งานจริง
Docker เป็นหนึ่งในแพลตฟอร์มยอดนิยมสำหรับการพัฒนาและปรับใช้ซอฟต์แวร์ ซอฟต์แวร์แพ็คเกจ Docker เป็น "อิมเมจ" ซึ่งเปลี่ยนเป็นคอนเทนเนอร์ขณะรันไทม์เมื่อดำเนินการบน Docker Image การแยกนี้ช่วยให้นักพัฒนาสามารถเรียกใช้คอนเทนเนอร์จำนวนมากบนโฮสต์เดียวได้พร้อมกัน
นักพัฒนา Rails เผชิญกับความท้าทายที่ไม่เหมือนใครเมื่อทำคอนเทนเนอร์แอปพลิเคชันที่มีอยู่ บทความนี้จะให้คำแนะนำเกี่ยวกับการบรรจุแอป Rails ที่ใช้งานได้จริง และอธิบายแนวคิดและข้อผิดพลาดที่สำคัญไปพร้อมกัน บทความนี้ไม่ใช่คำอธิบายพื้นฐานของคอนเทนเนอร์หรือนักเทียบท่า แต่เป็นการอธิบายปัญหาที่นักพัฒนาต้องเผชิญเมื่อคอนเทนเนอร์แอปพลิเคชันที่ใช้งานจริง
ข้อกำหนดเบื้องต้น
หากคุณกำลังติดตามคุณจะต้องมีแอปพลิเคชัน Rails ที่ยังไม่ได้เทียบท่า (นั่นคือคำศัพท์เฉพาะของนักเทียบท่าสำหรับ 'คอนเทนเนอร์') ฉันจะใช้ RailsWork ซึ่งเป็นโครงการด้านที่มีคุณลักษณะครบถ้วนที่ฉันเพิ่ง เปิดตัว เป็นบอร์ดงานที่เขียนด้วย Rails และปรับใช้กับ Heroku แต่ไม่ได้บรรจุในคอนเทนเนอร์
นอกจากนั้น คุณจะต้องติดตั้ง Docker ด้วย วิธีที่นิยมในการติดตั้งคือใช้ Docker Desktop ซึ่งสามารถดาวน์โหลดได้จากเว็บไซต์ทางการ
เมื่อดาวน์โหลดแอปแล้ว ให้เรียกใช้โปรแกรมติดตั้ง หลังจากรัน มันจะแจ้งให้คุณลากแอปพลิเคชันไปยังโฟลเดอร์แอปพลิเคชันของคุณ จากนั้น คุณจะต้องเปิดแอป จากโฟลเดอร์แอปพลิเคชันของคุณ และให้สิทธิ์พิเศษตามที่ร้องขอ เพื่อเป็นการตรวจสอบครั้งสุดท้ายเพื่อให้แน่ใจว่า Docker ได้รับการติดตั้งอย่างถูกต้อง ให้ลองแสดงรายการคอนเทนเนอร์ที่ทำงานบนเครื่องของคุณจากเทอร์มินัลของคุณโดยเรียกใช้สิ่งต่อไปนี้:
docker ps
หากมีการติดตั้ง Docker (และคุณไม่ได้ใช้งานคอนเทนเนอร์ใดๆ) คุณจะได้รับรายการว่างพร้อมส่วนหัวที่มีลักษณะดังนี้:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
The Dockerfile
สิ่งสำคัญคือต้องเริ่มต้นด้วยคำศัพท์ที่ชัดเจนก่อนที่เราจะเจาะลึกเกินไป
หลังจากที่แอปพลิเคชัน Rails ของคุณ "เชื่อมต่อแล้ว" แอปพลิเคชันจะทำงานใน คอนเทนเนอร์ . คอนเทนเนอร์ยืนอยู่คนเดียว เปลี่ยนได้ และสร้างใหม่ได้บ่อยครั้ง
คอนเทนเนอร์สร้างขึ้นจากอิมเมจ . รูปภาพคือสแน็ปช็อตเสมือนของระบบไฟล์ที่จับคู่กับข้อมูลเมตา
Dockerfile คือซอร์สโค้ดที่อธิบายวิธีการสร้างภาพ ไฟล์ Docker มักจะรวมอยู่ในที่เก็บของแอพ Dockerized และติดตามในการควบคุมเวอร์ชันพร้อมกับแอปที่เหลือ
การสร้าง Dockerfile นั้นง่ายกว่าเสียง! Docker ให้ไวยากรณ์พิเศษแก่เราที่แยกการทำงานหนักของคอนเทนเนอร์บางอย่างออกไป ขั้นแรก ให้ไปที่ไดเร็กทอรีรากของแอปที่คุณต้องการคอนเทนเนอร์ เมื่อคุณพร้อมที่จะเริ่มทำงานแล้ว คุณควรสร้างสาขาใหม่หากคุณใช้ git คุณสามารถสร้างสาขาใหม่ได้อย่างง่ายดายด้วยชื่อ dockerize-this-app
โดยเรียกใช้สิ่งต่อไปนี้:
git checkout -b dockerize-this-app
จากนั้น สร้าง Dockerfile และสั่งให้สร้างอิมเมจตามแอปพลิเคชัน Ruby ซึ่งสามารถทำได้จากบรรทัดคำสั่งโดยเรียกใช้สิ่งต่อไปนี้:
echo "FROM ruby:3.0.0" > Dockerfile
ที่นี่ เราเพิ่งสร้าง Dockerfile และเพิ่มบรรทัดที่ระบุตำแหน่งที่จะค้นหาอิมเมจคอนเทนเนอร์ Ruby โปรเจ็กต์ของฉันใช้ Ruby 3.0.0 ดังนั้นฉันจึงใช้รูปภาพที่เหมาะสม หากคุณใช้ Ruby เวอร์ชันอื่น ก็ไม่มีปัญหา Docker มีรายการรูปภาพที่รองรับทั้งหมด
ถัดไป สั่งให้ Docker สร้างอิมเมจ Docker ด้วยตนเอง:
docker build -t rails_work .
ที่นี่ คุณสามารถแทนที่ rails_work
ด้วยชื่อที่คุณต้องการสำหรับภาพ และอย่าลืมใส่จุดต่อท้ายด้วย!
หากคุณต้องการดูว่ารูปภาพถูกสร้างขึ้นแล้ว คุณสามารถแสดงรายการรูปภาพในระบบของคุณได้ดังนี้:
docker image list
ภาพนี้ส่วนใหญ่ว่างเปล่าแม้ว่า; ขณะนี้ยังไม่มีแอปพลิเคชันของเรา เราสามารถสั่งให้มันเพิ่มโค้ดจากแอพของเราโดยเพิ่มสิ่งต่อไปนี้ใน Dockerfile ต่อท้าย:
ADD . /rails_work
WORKDIR /rails_work
RUN bundle install
การดำเนินการนี้จะคัดลอกไฟล์จากแอปพลิเคชันของคุณและติดตั้งการขึ้นต่อกันของแอปพลิเคชัน (ที่นี่ คุณจะแทนที่ rails_work
ด้วยชื่อแอปของคุณ)
ณ จุดนี้ คุณควรรันคำสั่งอีกครั้งเพื่อสร้างภาพ:
docker image list
มีความเป็นไปได้สำหรับปัญหาที่นี่ โดยเฉพาะอย่างยิ่ง หากคุณกำลังทำสิ่งนี้กับแอปพลิเคชันที่ใช้งานจริงที่มีอยู่ Bundler อาจบ่นว่าเวอร์ชันของ Bundler ที่รูปภาพพยายามใช้นั้นแตกต่างจากที่สร้าง Gemfile.lock
ไฟล์. หากเกิดเหตุการณ์นี้ คุณมีสองตัวเลือกที่ชัดเจน:
- เปลี่ยนเวอร์ชันที่ใช้รูปภาพ
- ลบ
Gemfile.lock
โดยสิ้นเชิง **หากคุณทำเช่นนี้ ตรวจสอบให้แน่ใจว่าได้ปักหมุดเวอร์ชันของอัญมณีที่คุณต้องการในเวอร์ชันเฉพาะ เนื่องจากไฟล์ล็อคจะถูกสร้างขึ้นใหม่ทั้งหมด
หากการติดตั้งบันเดิลของคุณยังคงล้มเหลว คุณอาจต้องติดตั้งเพิ่มเติมใน Dockerfile :
RUN apt-get update && apt-get install -y shared-mime-info
หากคุณยังคงประสบปัญหา คุณอาจเลือกรูปภาพ Ruby ที่ไม่ถูกต้องเพื่อใช้เป็นพื้นฐาน ดังนั้นจึงควรเริ่มการสอบสวนที่นั่น
นี่เป็นโอกาสที่ดีในการตั้งค่าตัวแปรสภาพแวดล้อม :
ENV RAILS_ENV production
ENV RAILS_SERVE_STATIC_FILES true
ถัดไป เพิ่มบรรทัดเพื่อแสดงพอร์ต 3000 ซึ่งเป็นที่ที่ Rails ทำงานโดยค่าเริ่มต้น:
EXPOSE 3000
สุดท้าย สั่งให้คอนเทนเนอร์เปิด bash shell เมื่อเริ่มทำงาน:
CMD ["bash"]
โดยรวมแล้ว Dockerfile ของคุณควรมีลักษณะดังนี้ (โดยแทนที่ชื่อ rails_work):
FROM ruby:3.0.0
ADD . /rails_work
WORKDIR /rails_work
RUN bundle install
ENV RAILS_ENV production
ENV RAILS_SERVE_STATIC_FILES true
EXPOSE 3000
CMD ["bash"]
คำอธิบายคำสั่งนักเทียบท่า
จะช่วยให้เข้าใจคำสั่ง Dockerfile ที่พบบ่อยที่สุดบางส่วนได้อย่างแน่นอน
- FROM -> เป็นตัวกำหนดว่าภาพใดที่จะใช้เป็นฐาน
- RUN -> ดำเนินการคำสั่ง ภายใน ภาชนะ
- ENV -> สิ่งนี้กำหนดตัวแปรสภาพแวดล้อม
- WORKDIR -> สิ่งนี้จะเปลี่ยนไดเร็กทอรีที่คอนเทนเนอร์ใช้อยู่
- CMD -> ระบุโปรแกรมที่จะรันเมื่อคอนเทนเนอร์เริ่มทำงาน
นักเทียบท่าเขียน
ตามเอกสารของ Docker "เขียน" เป็นเครื่องมือสำหรับสร้าง (และเริ่มต้น) แอปพลิเคชันที่มีคอนเทนเนอร์ Docker หลายตัว ทุกสิ่งที่จำเป็นในการรวมคอนเทนเนอร์ที่จำเป็นของแอปพลิเคชันจะได้รับการสรุปไว้ใน YAML เมื่อมีคนเรียกใช้ docker-compose up
, คอนเทนเนอร์ถูกสร้างขึ้น! Docker-compose ช่วยให้เราอธิบายการกำหนดค่าคอนเทนเนอร์ของเราได้อย่างชัดเจน
ก่อนสร้างไฟล์ Docker Compose คุณต้องระบุให้ Docker ทราบว่าไฟล์ใดควรแยกออกจากรูปภาพที่สร้างขึ้น สร้างไฟล์ชื่อ .dockerignore
. (สังเกตจุด!) ในไฟล์นี้ ให้วางสิ่งต่อไปนี้:
.git
.dockerignore
.env
หาก Gemfile ของคุณได้รับการดูแลโดยกระบวนการสร้าง อย่าลืมเพิ่ม Gemfile.lock
เพื่อละเว้นข้างต้น
จากนั้น สร้างไฟล์ชื่อ docker-compose.yml
. นี่คือที่ที่เราจะอธิบายการกำหนดค่าคอนเทนเนอร์ของเรา เราจะเริ่มด้วยสิ่งนี้สำหรับเนื้อหาของไฟล์:
version: '3.8'
services:
db:
image: postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- postgres:/var/lib/postgresql/data
web:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/Rails-Docker
ports:
- "3000:3000"
depends_on:
- db
volumes:
postgres:
ไฟล์นี้สร้างสองบริการ:บริการหนึ่งเรียกว่า db
และอีกอันเรียกว่า web
. คอนเทนเนอร์ db จะถูกสร้างขึ้นจากอิมเมจที่สร้างไว้ล่วงหน้าสำหรับ postgres และคุณควรแทนที่ค่าที่เกี่ยวข้องสำหรับ POSTGRES_USER
และ POSTGRES_PASSWORD
. คุณควร ระวังอย่าใส่ความลับในการผลิต ในไฟล์นี้ - ดูข้อมูลเพิ่มเติมในส่วน "การจัดการความลับ" ด้านล่าง
เว็บคอนเทนเนอร์สร้างขึ้นจาก Dockerfile ของเรา จากนั้นจึงเริ่มเซิร์ฟเวอร์ Rails บนพอร์ต 3000 ที่ที่อยู่ IP 0.0.0.0 จากนั้นพอร์ตภายใน 3000 จะจับคู่กับพอร์ต 3000 จริง
และสุดท้าย เรามีโวลุ่ม Postgres เพื่อยืนยันข้อมูลของเรา
การจัดการความลับ
การตรวจสอบสิทธิ์ ณ เวลาสร้างอาจเป็นปัญหาสำหรับแอปพลิเคชันที่ใช้งานจริง บางทีแอปพลิเคชันของคุณอาจค้นหา Gems จากที่เก็บส่วนตัว หรือคุณเพียงแค่ต้องจัดเก็บข้อมูลรับรองฐานข้อมูล
ข้อมูลใดๆ ที่อยู่ใน Dockerfile โดยตรงจะถูกอบลงในอิมเมจคอนเทนเนอร์ตลอดไป และนี่คือหลุมพรางด้านความปลอดภัยทั่วไป
หากคุณใช้ตัวจัดการข้อมูลประจำตัวของ Rails การให้สิทธิ์เข้าถึง Docker (หรือโฮสต์สำหรับเรื่องนั้น) นั้นค่อนข้างไม่สำคัญ ในไฟล์ Docker Compose คุณเพียงแค่ระบุ RAILS_MASTER_KEY
ตัวแปรสภาพแวดล้อม สำหรับเป้าหมายการเขียนที่กำหนด คุณระบุคีย์ภายใต้ environment
ส่วนหัวซึ่งคุณต้องสร้างหากยังไม่ได้สร้าง ไฟล์นักเทียบท่าจากด้านบนจะกลายเป็นดังต่อไปนี้:
version: '3.8'
services:
db:
image: postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- postgres:/var/lib/postgresql/data
web:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/Rails-Docker
ports:
- "3000:3000"
depends_on:
- db
environment:
- RAILS_MASTER_KEY=this_would_be_the_key
volumes:
postgres:
ตอนนี้มันทิ้งคุณไว้ที่ทางแยก คุณอาจต้องการให้ไฟล์นี้ผูกมัดกับการควบคุมต้นทาง แต่คุณ ไม่ต้องการให้มาสเตอร์คีย์ของคุณหรือแม้แต่รหัสผ่านฐานข้อมูลของคุณถูกติดตามโดยตัวควบคุมต้นทางอย่างแน่นอน เนื่องจากนี่อาจเป็นปัญหาด้านความปลอดภัยที่เป็นอันตรายอีกประการหนึ่ง ทางออกที่ดีที่สุดคือการใช้ dotenv gem เพื่อให้คุณสามารถเข้าถึงข้อมูลประจำตัวเหล่านี้โดยพร็อกซี เก็บไว้ใน ไฟล์อื่น ที่ไม่ได้ติดตามโดยการควบคุมแหล่งที่มา
การเรียกใช้แอปพลิเคชัน Dockerized
สุดท้าย คุณสามารถเรียกใช้แอปพลิเคชันเทียบท่าด้วยคำสั่งต่อไปนี้:
docker compose up
เชื่อหรือไม่ แค่นั้นแหละ! Docker-compose ทำให้การหมุนคอนเทนเนอร์เป็นเรื่องง่าย โดยเฉพาะอย่างยิ่งเมื่อพูดถึงอาร์กิวเมนต์บรรทัดคำสั่ง
หากคุณต้องการรายการคอนเทนเนอร์ที่ทำงานอยู่ ให้เรียกใช้สิ่งต่อไปนี้:
docker ps
หากชื่อคอนเทนเนอร์ Rails ของคุณคือ web
คุณสามารถรันคำสั่งได้ด้วยวิธีที่ค่อนข้างตรงไปตรงมา ตัวอย่างเช่น หากคุณต้องการเรียกใช้คอนโซล Rails สิ่งที่คุณต้องทำคือเรียกใช้สิ่งต่อไปนี้:
docker exec -it web rails console
หากคุณต้องการแค่ bash shell ภายในคอนเทนเนอร์ ให้เรียกใช้สิ่งต่อไปนี้แทน:
docker exec -it web bash
ข้อผิดพลาดบางอย่างเช่นที่ระบุไว้ที่นี่
ปัญหาทั่วไปอย่างหนึ่งของแอปพลิเคชัน Rails ที่เทียบชิดขอบในการผลิตคือการจัดการกับบันทึก ไม่ควรอยู่ในระบบคอนเทนเนอร์ในระยะยาว นักเทียบท่าแนะนำว่าบันทึกเพียงเปลี่ยนเส้นทางไปยัง STDOUT สามารถกำหนดค่าได้อย่างชัดเจนใน config/application.rb
.
ปัญหาทั่วไปอีกประการหนึ่งคือเรื่องจดหมาย หากแอปพลิเคชันของคุณใช้จดหมาย คุณต้องกำหนดการตั้งค่าการเชื่อมต่ออย่างชัดเจน . SMTP เป็นวิธีการจัดส่งที่สมบูรณ์แบบและมักจะทำงานได้ดีกับค่าเริ่มต้น แต่เราต้องระวังในการตั้งค่าตำแหน่งเซิร์ฟเวอร์และการตั้งค่าอื่นๆ เพื่อให้ตรงกับการกำหนดค่าคอนเทนเนอร์ของเรา
หากคุณมีผู้ปฏิบัติงานหรืองานเบื้องหลัง เช่น sidekiq จากนั้นคุณต้องเรียกใช้ในคอนเทนเนอร์ของตัวเอง
บทสรุป
การบรรจุแอปพลิเคชัน Rails เวอร์ชันใช้งานจริงมาพร้อมกับความท้าทายต่างๆ อย่างที่คุณเคยเห็นมาอย่างไม่ต้องสงสัย เมื่อแอปพลิเคชันของคุณเติบโตขึ้น มีแนวโน้มว่าจะได้สะสมการพึ่งพาจำนวนมากซึ่งทำให้การย้ายข้อมูลเช่นนี้มีความท้าทาย ไม่ว่าจะเป็นพนักงานที่ทำงานเบื้องหลัง จดหมาย หรือความลับ มีรูปแบบที่กำหนดไว้เพื่อจัดการกับข้อผิดพลาดส่วนใหญ่ เมื่องานเริ่มต้นในการรับแอปพลิเคชันที่ใช้งานจริงที่ทำงานกับ Docker เสร็จสมบูรณ์ ความง่ายในการเปลี่ยนแปลงและการปรับใช้ในอนาคตจะทำให้การลงทุนคุ้มค่า