Published on

Application Containerization Guide

Authors
  • avatar
    Name
    Yuchen Wei
    Twitter

1. Image Standards

1.1 Run as a Non-Root User

To enhance security, always run containers with a non-root user.

1.2 Use Platform-Provided Base Images

Our platform’s base images follow internal standards, incorporate security best practices, and receive regular updates.

1.3 Properly Handle PID 1, Signals, and Zombie Processes

Docker and Kubernetes communicate with containerized processes via signals, especially when terminating them. The process with PID 1 receives these signals. If the process does not handle signals correctly (e.g., using bash), it may cause termination issues and zombie processes.

Best Practices:

  1. Use applications like Nginx or Spring Boot that handle process signals correctly.
  2. Enable process namespace sharing.
  3. Use a dedicated init system to handle orphaned processes.

2. Application Health Checks

The liveness probe ensures the container is restarted when it becomes unresponsive while still running. This prevents hanging processes from affecting service availability.

2.2 Readiness Probe

The readiness probe verifies if a container has started successfully and is ready to accept traffic. If a Pod is not ready, it will not be included in the Service's iptables rules, preventing traffic from being sent to an unresponsive instance.


3. High Availability Deployment

3.1 Resource Limits

In production environments, set limits and requests to the same value. This ensures stable performance even when node resources are exhausted.


4. Logging Standards

Logs must be written to persistent storage to meet compliance requirements.


5. Dockerfile Best Practices

# Use platform-specific base image
FROM node:14 as builder
WORKDIR /app
COPY ./ /app/

# Install dependencies
RUN npm config set sass_binary_site=https://registry.npmjs.org/node-sass \
    && npm install --registry https://registry.npmjs.org/

# Set environment variables
ENV NODE_ENV=production \
    VUE_APP_CDN_ADMIN_DEFAULT_TIMEOUT=ADMIN_DEFAULT_TIMEOUT

# Build application
RUN npm run build

# Use Nginx as the final image
FROM nginx:1.16.1
WORKDIR /app

# Copy built files
COPY --from=builder /app/dist /usr/share/nginx/html
COPY --from=builder /app/nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /app/run.sh /app/run.sh

# Set environment defaults
ENV USER_CENTER_URL=http://user-center:9050 \
    CONFIG_CENTER_URL=http://apollo-adminservice:8090

# Set permissions
RUN chown -R nginx:nginx /app /usr/share/nginx /var/cache/nginx /var/log/nginx /etc/nginx \
    && chmod -R 777 /app /usr/share/nginx /var/cache /var/log /etc/nginx

USER nginx

# Expose port and start service
EXPOSE 8080
CMD /app/run.sh && nginx -g 'daemon off;'

6. Capacity Planning

Perform load testing to determine the TPS (transactions per second) capacity of a single replica before setting replica counts. By default, at least two replicas should be deployed.

For critical workloads, conduct load testing in production (preferably during off-peak hours) to validate scaling assumptions.


  1. External to Cluster: Use NodePort (Layer 4) or Ingress (Layer 7) for secure external service access.
  2. Internal Cluster Communication: Prefer Service (Layer 4) or Ingress (Layer 7) for inter-service communication.
  3. Cluster to External: Directly configure external dependencies (e.g., MySQL connection strings) as in traditional deployments.