Dockerfile Mastery | Lean & Secure Docker Images | Sinhala Tutorial

ආයුබෝවන්, Software Engineering ලෝකයේ ඉන්න අපේ හැමෝටම!
අද කාලේ Software Development කියන්නේ කලින් තිබ්බට වඩා ගොඩක් වෙනස් වෙලා. Microservices, Cloud Native Applications වගේ දේවල් නිසා අපේ Applications හදන විදිහ වගේම Deploy කරන විදිහත් ගොඩක් වෙනස් වෙලා. මේ හැමදේකම පාහේ මැදින් ඉන්න ප්රධානම කෙනෙක් තමයි Docker කියන්නේ.
ඔයාලා Docker ගැන අහලා ඇති, සමහර විට පාවිච්චි කරලත් ඇති. හැබැයි Docker වල හදවත වගේ ක්රියා කරන Dockerfile එකක් හරියට, කාර්යක්ෂමව සහ ආරක්ෂිතව ලියන්න දන්නවද? මේක Software Development Lifecycle එකේ ගොඩක් වැදගත් අංගයක්. Docker Image එකක Size එක අඩු කරගන්න එකේ ඉඳලා, Security Vulnerabilities අඩු කරගන්න එක දක්වා ගොඩක් දේවල් තීරණය වෙන්නේ අපි ලියන Dockerfile එක මත.
මේ Sinhala Guide එකෙන් අපි Dockerfile එකක් ලියන මුලිකම දේවල් වලින් පටන් අරන්, ඒකේ තියෙන Advanced Concepts, Best Practices සහ Security Aspects ගැනත් කතා කරනවා. මේක කියෙව්වට පස්සේ ඔයාලට පුළුවන් වෙයි ඔයාලගේ Applications වලට වඩාත් හොඳ, Production-Ready Dockerfiles ලියන්න. එහෙනම් අපි පටන් ගමුද?
Dockerfile වල මූලික අංග (The Basics of Dockerfiles)
Dockerfile එකක් කියන්නේ, අපිට ඕන කරන Docker Image එක හදන්න Docker ට දෙන Instructions ලිස්ට් එකක්. මේ Instructions ටික එකින් එක Execute කරලා තමයි Docker Image එක හැදෙන්නේ. අපි බලමු ප්රධානම Instructions ටික මොනවද කියලා.
FROM Instruction
ඕනෑම Dockerfile එකක මුලින්ම තියෙන්න ඕන Instruction එක තමයි FROM
කියන්නේ. මේකෙන් කරන්නේ අපේ Image එක හදන්න පාවිච්චි කරන Base Image එක මොකක්ද කියලා Docker ට කියන එක. මේ Base Image එක තමයි අපේ Application එක දුවන්න ඕන Operating System එකේ ඉඳලා Runtime එක දක්වා අවශ්ය කරන හැමදේම සපයන්නේ. Node.js, Python, Ubuntu, Alpine වගේ ගොඩක් Official Base Images Docker Hub එකේ තියෙනවා.
FROM node:18-alpine
මේ Example එකෙන් කියන්නේ අපි Node.js 18 Runtime එක තියෙන Alpine Linux Base Image එකක් පාවිච්චි කරනවා කියන එක. Alpine කියන්නේ ගොඩක් පොඩි, ආරක්ෂිත Linux Distribution එකක්, ඒ නිසා Docker Images වලට ගොඩක් හොඳයි.
RUN Instruction
RUN
Instruction එකෙන් කරන්නේ Docker Image Build වෙන වෙලාවේදී Commands Execute කරන එක. මේ Commands වලින් තමයි අපේ Application එකට අවශ්ය කරන Dependencies Install කරන්නේ, Build Process එකක් කරනවා නම් ඒක කරන්නේ, නැත්නම් වෙනත් Setup Tasks කරන්නේ. හැම RUN
Instruction එකක්ම අලුත් Layer එකක් විදිහට Cache වෙනවා.
RUN npm install
මේකෙන් කියන්නේ npm install කියන Command එක Build Time එකේදී Execute කරන්න කියන එක. සාමාන්යයෙන් අපේ Node.js Application එකක package.json එකේ තියෙන Dependencies Install කරගන්න මේක පාවිච්චි කරනවා.
COPY සහ ADD Instructions
මේ Instructions දෙකම පාවිච්චි කරන්නේ අපේ Local Machine එකේ තියෙන Files සහ Folders Docker Image එකට Copy කරන්න. දෙකේම ප්රධාන Syntax එක SOURCE_PATH DESTINATION_PATH
වගේ.
COPY
: මේකෙන් කරන්නේ අපේ Local Machine එකේ තියෙන Files සහ Directories Docker Image එකේ තියෙන Destination Path එකට Copy කරන එක. මේක Simple File Copying වලට වඩාත් සුදුසුයි.ADD
:COPY
එකට වඩා ටිකක් Advanced. මේකෙන් Local Files Copy කරන්න පුළුවන් වගේම, URL එකකින් Files Download කරන්නත්, Compressed Files (Tarballs) Automatically Extract කරන්නත් පුළුවන්. හැබැයි Best Practice විදිහට, URL වලින් Files Download කරන්නwget
හෝcurl
වගේ Command එකක් එක්කRUN
පාවිච්චි කරන එක වඩා හොඳයි, Transparency සහ Cache Invalidation පාලනය කරගන්න. ඒ වගේම Compressed Files Extract කරන්නත්RUN tar -xf ...
වගේ දෙයක් පාවිච්චි කරන එක තමයි සුදුසු. ඒ නිසා ගොඩක් වෙලාවටCOPY
පාවිච්චි කරන එක තමයි නිර්දේශ කරන්නේ.
COPY package*.json ./
COPY . .
මේකෙන් මුලින්ම package.json
සහ package-lock.json
(තියෙනවා නම්) Root Directory එකට Copy කරනවා. ඊට පස්සේ ඉතිරි Application Files Copy කරනවා. මේකෙන් Layer Caching වලට උදව් වෙනවා.
සරල Node.js Application එකක් සඳහා Dockerfile එකක් (Simple Node.js Application Dockerfile)
අපි දැන් මේකෙන් මේ ටික එකතු කරලා සරල Node.js Application එකක් Containerize කරන්න අවශ්ය Dockerfile එකක් ලියමු.
# 1. Base Image එක තෝරාගැනීම. Node.js 18 Alpine Version එක
FROM node:18-alpine
# 2. වැඩ කරන Directory එක සකස් කිරීම. මේකෙන් පස්සේ එන හැම Command එකක්ම මේ Directory එකේදී Execute වෙනවා.
WORKDIR /app
# 3. Application එකේ package.json files ටික Copy කරගන්නවා.
# මේක මුලින්ම Copy කරන්නේ Dependencies Install කරන්න.
# මේ Layer එකට නිතර වෙනස් වීම් අඩු නිසා Docker Cache එකට හොඳයි.
COPY package*.json ./
# 4. Dependencies Install කරනවා.
RUN npm install
# 5. Application එකේ ඉතිරි Files ටික Copy කරනවා.
COPY . .
# 6. Container එකේ Port 3000 expose කරනවා. මේක Documentation එකක් විතරයි, Firewall Open කරන්නේ නෑ.
EXPOSE 3000
# 7. Container එක Start කරනකොට Run වෙන්න ඕන Command එක.
CMD ["npm", "start"]
මේ Dockerfile එකත් එක්ක අපේ Node.js Application එක Build කරලා Run කරලා බලන්න පුළුවන්. Build කරන්න docker build -t my-node-app .
වගේ Command එකක් පාවිච්චි කරන්න, Run කරන්න docker run -p 80:3000 my-node-app
වගේ Command එකක් පාවිච්චි කරන්න පුළුවන්.
Command Executing Directives (CMD vs ENTRYPOINT)
CMD
සහ ENTRYPOINT
කියන Instructions දෙකම පාවිච්චි කරන්නේ Docker Container එකක් Start කරන වෙලාවේදී Command එකක් Execute කරන්න. හැබැයි මේ දෙක අතර පොඩි වෙනසක් තියෙනවා, ඒක හරියට තේරුම් ගන්න එක ගොඩක් වැදගත්.
CMD Instruction
CMD
එකෙන් Container එක Start කරනකොට Run වෙන්න ඕන Default Command එක සපයනවා. මේක ආකාර තුනකින් ලියන්න පුළුවන්:
CMD ["executable","param1","param2"]
(exec form - නිර්දේශිතයි)CMD ["param1","param2"]
(as default parameters to ENTRYPOINT)CMD command param1 param2
(shell form)
ගොඩක් වෙලාවට අපේ Application එක Start කරන Command එක දෙන්න CMD
පාවිච්චි කරනවා. උදාහරණයක් විදිහට Node.js Application එකක් නම්:
CMD ["npm", "start"]
CMD
එකේ විශේෂත්වය තමයි User කෙනෙකුට docker run
Command එකත් එක්ක වෙනත් Command එකක් දුන්නොත්, CMD
එකේ තියෙන Default Command එක Override වෙනවා. උදාහරණයක් විදිහට:
docker run my-node-app echo "Hello from container"
මේකෙන් කරන්නේ my-node-app
Image එක Run කරලා npm start
Command එක වෙනුවට echo "Hello from container"
කියන Command එක Execute කරන එක.
ENTRYPOINT Instruction
ENTRYPOINT
Instruction එකත් Container එක Start කරනකොට Run වෙන්න ඕන Command එකක් සපයනවා. හැබැයි CMD
වගේ මේක Override වෙන්නේ නෑ. ENTRYPOINT
එකෙන් Container එකේ Main Command එක Configure කරනවා, ඊට පස්සේ CMD
එකෙන් දෙන Parameters ඒ Main Command එකට Arguments විදිහට යවනවා.
මේකත් ගොඩක් වෙලාවට exec form එකෙන් තමයි ලියන්නේ:
ENTRYPOINT ["npm"]
CMD ["start"]
මේ Dockerfile එකෙන් Image එකක් Build කරලා docker run my-node-app
Command එක දුන්නොත්, Container එක ඇතුලේ npm start
Execute වෙනවා. හැබැයි docker run my-node-app install
වගේ Command එකක් දුන්නොත්, npm install
කියන Command එක Execute වෙනවා. මේකෙන් ENTRYPOINT
එකේ තියෙන Command එක Fixed කරලා, CMD
එකෙන් Arguments සපයන්න පුළුවන්. මේක ගොඩක් වෙලාවට Executable එකක් විදිහට Container එකක් පාවිච්චි කරනකොට ප්රයෝජනවත්.
CMD vs ENTRYPOINT: සාරාංශයක්
CMD
: Default Command එක සපයනවා. User Input එකකින් Override කරන්න පුළුවන්.ENTRYPOINT
: Container එකේ ප්රධාන Executable එක සපයනවා. User Input එකකින් Arguments සපයන්න පුළුවන්, නමුත් Executable එක වෙනස් කරන්න බෑ. Override කරන්න නම්docker run --entrypoint
Flag එක පාවිච්චි කරන්න වෙනවා.
Best Practice එක තමයි, Application එක Start කරන Command එකට CMD
එක පාවිච්චි කරන එක, Executable Wrapper Script එකක් වගේ දෙයක් හදන්න ENTRYPOINT
එක පාවිච්චි කරන එක.
කාර්යක්ෂම සහ ආරක්ෂිත Dockerfiles (Efficient and Secure Dockerfiles)
හොඳ Dockerfile එකක් කියන්නේ නිකන් Application එක Run වෙන එක විතරක් නෙවෙයි. ඒක Efficient වෙන්න ඕන (පොඩි Image Size එකක්, වේගයෙන් Build වෙන), ඒ වගේම Secure වෙන්නත් ඕන.
WORKDIR Instruction
WORKDIR
Instruction එකෙන් කරන්නේ Dockerfile එකේ ඊට පස්සේ එන RUN
, CMD
, ENTRYPOINT
, COPY
වගේ Instructions වලට Working Directory එක සකස් කරන එක. මේකෙන් අපේ Dockerfile එකේ Readability එක වැඩි වෙනවා වගේම, හැම වෙලාවෙම Absolute Paths දෙන්න අවශ්ය වෙන්නේ නෑ.
WORKDIR /app
COPY package*.json ./ # /app/package.json
RUN npm install # Executes inside /app
මේක කලින් අපි දැක්ක Dockerfile එකේත් තිබුණා. එක Dockerfile එකක WORKDIR
Instructions කිහිපයක් පාවිච්චි කරන්න පුළුවන්.
EXPOSE Instruction
EXPOSE
Instruction එකෙන් කරන්නේ Container එක Run වෙන Ports මොනවද කියලා Document කරන එක. මේක Network Ports Open කරන එකක් නෙවෙයි, Informative එකක් විතරයි. Port Mapping (-p
flag) කරන්නේ docker run
Command එකෙන්.
EXPOSE 3000
ENV Instruction
ENV
Instruction එකෙන් කරන්නේ Environment Variables සකස් කරන එක. මේවා Build Time එකේදී වගේම Run Time එකේදීත් Container එක ඇතුලේ පාවිච්චි කරන්න පුළුවන්. Database Connection Strings, API Keys වගේ දේවල් මේ විදිහට දෙන්න පුළුවන් (හැබැයි Sensitive Information නම් Secret Management Tools පාවිච්චි කරන එක තමයි නිර්දේශ කරන්නේ).
ENV NODE_ENV=production
ENV PORT=3000
මේවා පස්සේ docker run -e MY_VAR=value
වගේ Command එකකින් Override කරන්නත් පුළුවන්.
.dockerignore File එකේ වැදගත්කම
.dockerignore
කියන්නේ Git වල .gitignore
වගේ File එකක්. මේකෙන් කරන්නේ අපි Image එකට Copy කරන්න අවශ්ය නැති Files සහ Folders මොනවද කියලා Docker Daemon එකට කියන එක. මේක ගොඩක් වැදගත්:
- Build Context එක පොඩි කරගන්න (Build Time එක අඩු වෙනවා).
- අවශ්ය නැති Files Image එකට Copy වෙන එක වළක්වා ගන්න (Image Size එක අඩු වෙනවා).
- Sensitive Information (උදා:
.git
,node_modules
(සමහර වෙලාවට),.env
files) Image එකට යෑම වළක්වා ගන්න.
අපේ Node.js Application එකට .dockerignore
File එකක් ලිව්වොත් මේ වගේ වෙන්න පුළුවන්:
node_modules
.git
.gitignore
.env
Dockerfile
README.md
දැන් අපි COPY . .
වගේ Command එකක් දුන්නොත්, මේ Files සහ Folders Image එකට Copy වෙන්නේ නෑ.
ආරක්ෂිත Dockerfiles සඳහා තවත් Tips (More Tips for Secure Dockerfiles)
- Less Privileged User: හැම විටම
root
User විදිහට Application එක Run කරන එකෙන් වළකින්න.USER
Instruction එක පාවිච්චි කරලා අලුත් User කෙනෙක් හදලා ඒ User විදිහට Application එක Run කරන්න. - Minimal Base Images: Alpine වගේ පොඩි Base Images පාවිච්චි කරන්න. මේවායේ අනවශ්ය Packages අඩු නිසා Attack Surface එක අඩුයි.
- Avoid Installing Unnecessary Packages:
RUN
Command එකෙන් Application එකට අවශ්යම Packages විතරක් Install කරන්න. - Update Packages Regularly: Base Image එකේ සහ Application එකේ Dependencies වල Security Updates නිතරම කරන්න.
# ...
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
CMD ["npm", "start"]
Advanced Dockerfile Concepts (Multi-Stage Builds and Best Practices)
Dockerfile Mastery කියන්නේ මේ මූලික දේවල් වලට එහා ගිය Advanced Techniques පාවිච්චි කරන එක. Multi-Stage Builds කියන්නේ ඒ වගේ ගොඩක් වැදගත් Technique එකක්.
Multi-Stage Builds
Multi-Stage Builds පාවිච්චි කරන්නේ Docker Image Size එක අඩු කරගන්න සහ Build Process එකේදී අවශ්ය Build Tools සහ Dependencies, Final Production Image එකට යෑම වළක්වා ගන්න. හිතන්න Go Application එකක් වගේ Compiled Language එකකින් හදන Application එකක් ගැන. ඒක Build කරන්න Go Compiler එක වගේ ගොඩක් ලොකු Tools සෙට් එකක් අවශ්යයි. හැබැයි Application එක Run කරන්න ඕන Executable Binary එක විතරයි. එතකොට Multi-Stage Build එකකින් අපිට පුළුවන්:
- පළවෙනි Stage එකේදී Build Tools එක්ක Application එක Build කරන්න.
- දෙවෙනි Stage එකේදී Minimal Base Image එකක් අරගෙන, පළවෙනි Stage එකෙන් Build කරපු Final Executable එක විතරක් Copy කරගන්න.
මේකෙන් Final Image Size එක විශාල වශයෙන් අඩු කරගන්න පුළුවන්. අපේ Node.js Application එකට මේක පාවිච්චි කරන විදිහක් බලමු (මෙහිදී Build Stage එකක් එතරම් අවශ්ය නැතත්, Webpack වැනි Tools පාවිච්චි කරන්නේ නම් මෙය වැදගත් වේ).
# Stage 1: Build Stage (දැනට Node.js සඳහා සරලයි, නමුත් Compiled Languages වලට වඩාත් වැදගත්)
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install --production # Production dependencies පමණක් Install කරන්න
COPY . .
# Webpack, Babel වගේ Build Commands මෙතනට එන්න පුළුවන්
# RUN npm run build
# Stage 2: Production Stage
FROM node:18-alpine
WORKDIR /app
# Previous Stage එකෙන් අවශ්ය Dependencies සහ Application Code Copy කරගන්න
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/. ./
EXPOSE 3000
CMD ["npm", "start"]
මේ Example එකෙන් අපි node_modules Install කරලා තියෙන්නේ builder stage එකේදී. ඊට පස්සේ production stage එකේදී අලුත් minimal base image එකක් අරගෙන, builder stage එකෙන් node_modules සහ Application Files විතරක් Copy කරගන්නවා. මේකෙන් Development Dependencies Final Image එකට යෑම වළක්වනවා. Go හෝ Java වගේ Languages වලදී මේකේ වාසිය ගොඩක් පැහැදිලිව පෙනේවි.
Dockerfile Best Practices
Lean සහ Efficient Docker Images හදන්න තව Best Practices කිහිපයක් තියෙනවා:
- Order of Instructions for Layer Caching: Dockerfile එකේ Instructions ලියන අනුපිළිවෙල ගොඩක් වැදගත්. නිතර වෙනස් නොවන Instructions (උදා:
FROM
,COPY package.json
,RUN npm install
) මුලින්ම ලියන්න. නිතර වෙනස් වන Instructions (උදා:COPY . .
) අගට ලියන්න. මේකෙන් Docker Build කරනකොට Cache එක හරියට පාවිච්චි කරන්න පුළුවන්. - Use Specific Tags for Base Images:
node:latest
වගේ Tags පාවිච්චි කරනවාට වඩාnode:18-alpine
වගේ Specific Tags පාවිච්චි කරන්න. මේකෙන් Builds Consistent වෙනවා වගේම Unexpected Breaking Changes වළක්වා ගන්න පුළුවන්. - Minimize Image Layers: හැම Instruction එකක්ම අලුත් Layer එකක් හදනවා. හැකි තරම් Instructions එකතු කරලා Layers ගණන අඩු කරන්න. (උදා:
RUN
Commands එකතු කිරීම).
Combine RUN Commands: එක RUN
Instruction එකක් ඇතුලේ &&
පාවිච්චි කරලා Commands කිහිපයක් එකට එකතු කරන්න. මේකෙන් Image Layer ගණන අඩු කරගන්න පුළුවන්, Image Size එකත් අඩු වෙනවා.
# නරක විදිහක්
RUN apt-get update
RUN apt-get install -y some-package
# හොඳ විදිහක්
RUN apt-get update && \
apt-get install -y some-package && \
rm -rf /var/lib/apt/lists/*
අන්තිම rm -rf /var/lib/apt/lists/*
වගේ Command එකකින් කරන්නේ Package Cache Clear කරන එක. මේකෙන් Image Size එක තවත් අඩු කරගන්න පුළුවන්.
Security Scanning for Docker Images
අපි කොච්චර හොඳට Dockerfile එකක් ලිව්වත්, අපේ Base Images වල, Dependencies වල Security Vulnerabilities තියෙන්න පුළුවන්. ඒ නිසා Docker Images Security Scan කරන එක ගොඩක් වැදගත්.
- Trivy: මේක Open-Source Vulnerability Scanner එකක්. Image Layers ස්කෑන් කරලා Vulnerabilities හොයා ගන්නවා.
- Snyk: Snyk වගේ Commercial Tools වලින් Image Scan කරනවා වගේම, Dockerfile එකේ තියෙන Best Practice Violations පවා පෙන්නනවා.
- Docker Scan (powered by Snyk): Docker Desktop එකත් එක්ක එන
docker scan
Command එකෙන් Snyk Engine එක පාවිච්චි කරලා Images Scan කරන්න පුළුවන්.
මේ Tools පාවිච්චි කරලා අපේ Docker Images Production එකට Deploy කරන්න කලින් Scan කරන එක ගොඩක් වැදගත්.
නිගමනය (Conclusion)
හරි යාලුවනේ, මේ Sinhala Guide එකෙන් ඔයාලට Dockerfile එකක මුලික අංග වල ඉඳන් Advanced Concepts, Best Practices සහ Security Aspects ගැන හොඳ අවබෝධයක් ලැබෙන්න ඇති කියලා මම හිතනවා. Dockerfile එකක් කියන්නේ නිකන්ම Commands ලිස්ට් එකක් නෙවෙයි, ඒක අපේ Application එකේ Deployment Strategy එකේ හදවත වගේ දෙයක්.
අපි කතා කරපු FROM
, RUN
, COPY
, CMD
, ENTRYPOINT
වගේ Instructions වලින් අපේ Application එකට අවශ්ය Environment එක හදාගන්න පුළුවන්. ඒ වගේම .dockerignore
, Multi-Stage Builds, Layer Caching වගේ Techniques වලින් අපිට පුළුවන් Lean, Efficient සහ Secure Docker Images හදාගන්න. අමතක කරන්න එපා, Image Security Scan කරන එකත් අද කාලේ අනිවාර්ය දෙයක්.
ලංකාවේ Software Development Industry එක දියුණු වෙන මේ කාලේ, Containerization කියන Skill එක ගොඩක් වැදගත්. මේ දැනුමෙන් ඔයාලට පුළුවන් වෙයි ඔයාලගේ Projects වලට විශාල වටිනාකමක් එකතු කරන්න. දැන් ඔයාලා දන්නවා Dockerfile Mastery කියන්නේ මොකක්ද කියලා. ඒ නිසා මේ දේවල් ඔයාලගේ ඊලඟ Project එකට Implement කරලා බලන්න. මොන වගේ Challenges ආවද, මොන වගේ දේවල් අලුතින් ඉගෙන ගත්තද කියලා පහළින් Comment එකක් දාන්නත් අමතක කරන්න එපා!
ඔයාලගේ අදහස් සහ අත්දැකීම් දැනගන්න අපි ආසයි. එහෙනම් තවත් අලුත් Tutorial එකකින් හමුවෙමු!