Tổng hợp Dockerfile các dự án thực tế – chi tiết từng loại công nghệ
Hello mọi người. Lâu lắm mới viết bài lại chuẩn bị nhảy vào một cái bank làm culi trông và fix hạ tầng nên dọn máy có chút tài liệu tổng hợp Dockerfile thấy có thể sẽ hữu ích với mọi người nên mình có sao, dùng sao chia sẻ vậy. Dockerfile là một phần cực kỳ quan trọng trong Docker và cũng quá quen thuộc và nhiều tài liệu rồi nên mình chỉ đi vào cái thực tế mình đang dùng thôi nhé. Các ACE có gì góp ý để mình cải thiện và học hỏi nhau thêm nha 😄.
Làm sao để viết Dockerfile tốt
Đây là kinh nghiệm cá nhân mình thôi nha. Với mình Dockerfile tốt phải đáp ứng được:
- Điều số 1: Tương thích với hệ thống và mã nguồn (code), vì có những Dockerfile sử dụng các base Docker image nhẹ nhưng vô tình một số chức năng không hề hoạt động đúng như mong đợi (cái này trước đây mình gặp rồi khi đang chuẩn bị release cuống cuồng lên luôn – đề phòng đến già…)
- Điều số 2: Bảo mật, tuy nói rằng việc attack trực tiếp từ container vào đến hạ tầng server là cả vấn đề chứ không phải như các bộ phim nhưng dù sao tỷ lệ vẫn có và chủ yếu đến từ sự cẩn thận của người quản trị.
- Điều số 3: Nhẹ, chắc chắn rồi một Dockerfile tối ưu để build ra được Docker image thì bắt buộc yếu tố tối ưu về mặt kích thước là điều đương nhiên.
- Điều số 4: Nhanh, và cũng đương nhiên hiệu suất luôn là yếu tố cực kỳ quan trọng quyết định người dùng cảm thấy website của bạn “thoải mái” sử dụng hay không vì vậy mà Dockerfile chúng ta cũng phải thiết kế làm sao đáp ứng được điều này.
Dockerfile NodeJS
Cách 1. Cách này mình cũng khá hay dùng trên Production đó là chạy trực tiếp bằng npm
Dockerfile
# Stage 1: Build
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Stage 2: Run
FROM node:18-alpine
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app .
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["node", "server.js"]
Cách 2: Cách này mình sẽ chạy bằng pm2 về cơ bản thì mình thấy chạy bằng pm2 sẽ nhẹ hơn kha khá với npm
Dockerfile
# Stage 1: Build
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Stage 2: Run
FROM node:18-alpine
RUN npm install -g pm2
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app .
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["pm2-runtime", "start", "server.js"]
Bổ sung: Dockerfile cho ứng dụng Node.js (NestJS và Express.js)
Dockerfile NodeJS (NestJS)
# Stage 1: Build
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Run
FROM node:18-alpine
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/main"
Dockerfile NodeJS (Express.js)
# Stage 1: Build
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# Stage 2: Run
FROM node:18-alpine
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app .
EXPOSE 3000
CMD ["node", "index.js"]
Dockerfile ReactJS
Nói là Dockerfile cho ReactJS thôi nhưng mà bạn có thể làm cho hầu hết các framework frontend như Vue hay Angular vì bản chất nó khác mỗi một chút là ReactJS build ra thư mục build, Vue build ra thư mục dist. Và ở đây mình sẽ sử dụng web server là nginx chạy cho nhẹ cũng như có thể cấu hình một số thứ ở nginx để giúp kiểm soát tốt hơn về mặt bảo mật
Lưu ý: ở đây thư mục chứa nginx/nginx.conf đặt cùng cấp với Dockerfile
Dockerfile
# Stage 1: Build
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Run
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 3000
CMD ["nginx", "-g", "daemon off;"]
nginx.conf
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
server_tokens off;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
server {
listen 3000; # Lắng nghe trên cổng 3000
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, no-transform";
}
error_page 404 /404.html;
location = /404.html {
internal;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
internal;
}
location ~ /\.ht {
deny all;
}
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "no-referrer-when-downgrade";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'self';";
}
}
Dockerfile Java
Dockerfile cho Java thì nhiều, có Spring Boot, có Quarkus (JVM Mode, Native Mode) rồi còn dùng Maven, Gradle để build,…
Dockerfile cho Spring Boot (Maven build)
# Stage 1: Build
FROM maven:3.8.6-openjdk-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests
# Stage 2: Run
FROM openjdk:17-jdk-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
ENV JAVA_OPTS="-Xmx512m -Xms256m"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar app.jar"]
Dockerfile cho Spring Boot (Gradle build)
# Stage 1: Build
FROM gradle:7.5.1-jdk17 AS build
WORKDIR /app
COPY build.gradle .
COPY src ./src
RUN gradle build -x test
# Stage 2: Run
FROM openjdk:17-jdk-alpine
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar
ENV JAVA_OPTS="-Xmx512m -Xms256m"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar app.jar"]
Dockerfile cho Quarkus (JVM Mode)
# Stage 1: Build
FROM maven:3.8.6-openjdk-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests
# Stage 2: Run
FROM openjdk:17-jre-alpine
WORKDIR /app
COPY --from=build /app/target/quarkus-app/lib /app/lib
COPY --from=build /app/target/quarkus-app/*.jar /app/
COPY --from=build /app/target/quarkus-app/app /app/app
COPY --from=build /app/target/quarkus-app/quarkus /app/quarkus
ENV JAVA_OPTS="-Xmx512m -Xms256m"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app/quarkus-run.jar"]
Dockerfile cho Quarkus (Native Mode)
# Stage 1: Build
FROM quay.io/quarkus/centos-quarkus-maven:21.3.1-java17 AS build
WORKDIR /app
COPY src ./src
COPY pom.xml .
RUN mvn package -Pnative -DskipTests
# Stage 2: Run
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.6
COPY --from=build /app/target/*-runner /app/application
WORKDIR /app
EXPOSE 8080
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
Dockerfile Python
Python mình không dùng nhiều nhưng có dùng sao mình cũng node cụ thể lại cho mọi người nha
Dockerfile cho Python (Flask)
# Stage 1: Build
FROM python:3.9-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
COPY . .
# Stage 2: Run
FROM python:3.9-slim
WORKDIR /app
COPY --from=build /root/.local /root/.local
COPY --from=build /app .
ENV PATH=/root/.local/bin:$PATH
EXPOSE 5000
CMD ["flask", "run", "--host=0.0.0.0"]
Dockerfile cho Python (Django)
# Stage 1: Build
FROM python:3.9-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
COPY . .
# Stage 2: Run
FROM python:3.9-slim
WORKDIR /app
COPY --from=build /root/.local /root/.local
COPY --from=build /app .
ENV PATH=/root/.local/bin:$PATH
EXPOSE 8000
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
Dockerfile Laravel
Mình sẽ sử dụng Laravel với Nginx, PHP-FPM, và Supervisord
Lưu ý: thư mục conf/nginx.conf và conf/supervisord.conf cùng cấp với Dockerfile
Dockerfile
# Stage 1: Composer Dependencies
FROM composer:2 AS composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-interaction
# Stage 2: Build
FROM php:8.2-fpm-alpine AS php
WORKDIR /var/www/html
COPY --from=composer /app/vendor ./vendor
COPY . .
RUN apk add --no-cache \
libzip-dev \
libpng-dev \
libjpeg-turbo-dev \
freetype-dev \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) gd pdo_mysql zip opcache
RUN { \
echo 'opcache.enable=1'; \
echo 'opcache.revalidate_freq=0'; \
echo 'opcache.validate_timestamps=1'; \
echo 'opcache.max_accelerated_files=10000'; \
echo 'opcache.memory_consumption=192'; \
echo 'opcache.max_wasted_percentage=10'; \
echo 'opcache.interned_strings_buffer=16'; \
echo 'opcache.fast_shutdown=1'; \
} > /usr/local/etc/php/conf.d/opcache-recommended.ini
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
# Stage 3: Nginx và Supervisord
FROM nginx:alpine AS nginx
WORKDIR /var/www/html
COPY --from=php /var/www/html /var/www/html
COPY conf/nginx.conf /etc/nginx/conf.d/default.conf
RUN apk add --no-cache supervisor
COPY conf/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
EXPOSE 80
CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
nginx.conf
server {
listen 80;
server_name localhost;
root /var/www/html/public;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
}
supervisord.conf
[supervisord]
nodaemon=true
[program:php-fpm]
command=php-fpm
autostart=true
autorestart=true
stderr_logfile=/var/log/php-fpm.err.log
stdout_logfile=/var/log/php-fpm.out.log
[program:nginx]
command=nginx -g "daemon off;"
autostart=true
autorestart=true
stderr_logfile=/var/log/nginx.err.log
stdout_logfile=/var/log/nginx.out.log
Docker file NetCore
Đây là Dockerfile cho các ứng dụng .NET bạn chú ý trong cấu hình ứng dụng xem sử dụng port nào phù hợp nha
Dockerfile
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false
# Stage 2: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENV ASPNETCORE_URLS=http://+:80
ENV DOTNET_RUNNING_IN_CONTAINER=true
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
RUN find /app -name "*.pdb" -delete && \
find /app -name "*.xml" -delete
EXPOSE 80
ENTRYPOINT ["dotnet", "YourApp.dll"]
Dockerfile Go (Golang)
Go thì mình có làm qua vài dự án về Go Web Application, ACE có thể góp ý thêm giúp mình nha
Dockerfile
# Stage 1: Build
FROM golang:1.20-alpine AS build
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/myapp
# Stage 2: Run
FROM alpine:latest
WORKDIR /app
COPY --from=build /app/myapp /app/myapp
EXPOSE 8080
CMD ["/app/myapp"]
Dockerfile Ruby on Rails
Mình sẽ sử dụng Ruby on Rails với Puma và Nginx
Dockerfile
# Stage 1: Build
FROM ruby:3.2-alpine AS build
WORKDIR /app
RUN apk add --no-cache build-base nodejs yarn tzdata
COPY Gemfile Gemfile.lock ./
RUN bundle install --without development test
COPY . .
RUN bundle exec rails assets:precompile
# Stage 2: Run
FROM ruby:3.2-alpine
WORKDIR /app
COPY --from=build /app /app
RUN apk add --no-cache tzdata
EXPOSE 3000
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
Kết luận
Và đó là những Dockerfile mình có, nếu mình có làm nhiều hơn về các Tech khác mình sẽ update bài viết để giúp ACE được đầy đủ hơn.