# ---- Stage 1: Builder/Base ---- # This stage sets up the base environment, installs build tools, creates scripts and templates FROM ubuntu:22.04 AS builder ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Asia/Shanghai # Set the timezone RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && \ echo $TZ > /etc/timezone # Install all required packages in a single layer RUN apt-get update && \ apt-get install -y --no-install-recommends \ locales \ git \ nginx-full \ gettext-base && \ sed -i 's/# zh_CN.UTF-8 UTF-8/zh_CN.UTF-8 UTF-8/' /etc/locale.gen && \ locale-gen && \ update-locale LANG=zh_CN.UTF-8 && \ rm -rf /var/lib/apt/lists/* # Create non-root user for security RUN groupadd -r hexo && useradd -r -g hexo -d /home/hexo -s /bin/bash hexo # Make essential directories for artifacts RUN mkdir -p /root/hexo.git/hooks && \ mkdir -p /etc/container/templates && \ mkdir -p /app && \ mkdir -p /home/hexo # Configure git hook with proper permissions RUN git init --bare /root/hexo.git && \ echo "#!/bin/bash" > /root/hexo.git/hooks/post-receive && \ echo "git --work-tree=/home/www/hexo --git-dir=/root/hexo.git checkout -f" >> /root/hexo.git/hooks/post-receive && \ echo "chown -R hexo:hexo /home/www/hexo" >> /root/hexo.git/hooks/post-receive && \ chmod +x /root/hexo.git/hooks/post-receive # Backup original nginx.conf RUN cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak # Create improved SSH Config Template RUN cat > /etc/container/templates/sshd_config.template << 'EOF' # SSH Configuration Template Port ${SSH_PORT:-22} ListenAddress 0.0.0.0 ListenAddress :: PermitRootLogin ${PERMIT_ROOT_LOGIN:-no} PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys PasswordAuthentication no ChallengeResponseAuthentication no UsePAM yes X11Forwarding no PrintMotd no AcceptEnv LANG LC_* Subsystem sftp /usr/lib/openssh/sftp-server EOF # Create Nginx Config Template with security headers RUN cat > /etc/container/templates/nginx.conf.template << 'EOF' # Nginx Configuration Template user ${NGINX_USER:-hexo}; worker_processes ${NGINX_WORKERS:-auto}; pid /var/run/nginx.pid; events { worker_connections ${NGINX_CONNECTIONS:-1024}; use epoll; multi_accept on; } http { include /etc/nginx/mime.types; default_type application/octet-stream; # Logging access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; # Performance sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; # Security headers add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; # Gzip compression gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; server { listen ${HTTP_PORT:-80}; server_name ${SERVER_NAME:-localhost}; root ${WEB_ROOT:-/home/www/hexo}; index index.html index.htm; # Security server_tokens off; location / { try_files $uri $uri/ =404; } # Static files caching location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 1y; add_header Cache-Control "public, immutable"; } } } EOF # Create improved start script RUN cat > /app/start.sh << 'EOF' #!/bin/bash # Color definitions RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Log file and rotation settings LOG_DIR="/var/log/container" LOG_FILE="$LOG_DIR/services.log" MAX_LOG_SIZE=10485760 # 10MB # --- Logging functions --- _log() { local level_color=$1 local level_name=$2 shift 2 echo -e "${level_color}[${level_name}]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $@" } log_info() { _log "$BLUE" "INFO" "$@"; } log_success() { _log "$GREEN" "SUCCESS" "$@"; } log_warning() { _log "$YELLOW" "WARNING" "$@"; } log_error() { _log "$RED" "ERROR" "$@"; } # --- Setup logging --- setup_logging() { mkdir -p "$LOG_DIR" touch "$LOG_FILE" log_info "Logging to console and $LOG_FILE" exec > >(tee -a "$LOG_FILE") 2> >(tee -a "$LOG_FILE" >&2) } # --- Render configuration files --- render_config() { log_info "Rendering configuration templates..." local rendered=0 if envsubst < /etc/container/templates/sshd_config.template > /etc/ssh/sshd_config; then log_success "SSHD configuration rendered"; ((rendered++)) else log_error "Failed to render SSHD configuration"; fi if envsubst < /etc/container/templates/nginx.conf.template > /etc/nginx/nginx.conf; then log_success "Nginx configuration rendered"; ((rendered++)) else log_error "Failed to render Nginx configuration"; fi if [ "$rendered" -eq 2 ]; then log_success "All configuration files rendered" return 0 else log_error "Failed to render some configuration files" return 1 fi } # --- Start services --- start_services() { log_info "Starting SSH service..." if [ ! -f "/etc/ssh/ssh_host_rsa_key" ]; then log_info "Generating SSH host keys..." ssh-keygen -A fi /usr/sbin/sshd -D & SSH_PID=$! log_info "Starting Nginx service..." nginx -g "daemon off;" & NGINX_PID=$! sleep 2 if kill -0 $SSH_PID 2>/dev/null && kill -0 $NGINX_PID 2>/dev/null; then log_success "All services started successfully" return 0 else log_error "Failed to start some services" return 1 fi } # --- Monitor services --- monitor_services() { log_info "Starting service monitoring..." while true; do sleep 30 if ! kill -0 $SSH_PID 2>/dev/null; then log_error "SSH service stopped, attempting restart..." /usr/sbin/sshd -D & SSH_PID=$! fi if ! kill -0 $NGINX_PID 2>/dev/null; then log_error "Nginx service stopped, attempting restart..." nginx -g "daemon off;" & NGINX_PID=$! fi done } # --- Cleanup function --- cleanup() { log_info "Received shutdown signal, gracefully stopping services..." if [ ! -z "$NGINX_PID" ] && kill -0 $NGINX_PID 2>/dev/null; then log_info "Stopping Nginx (PID:$NGINX_PID)" kill -TERM $NGINX_PID wait $NGINX_PID 2>/dev/null log_success "Nginx stopped" fi if [ ! -z "$SSH_PID" ] && kill -0 $SSH_PID 2>/dev/null; then log_info "Stopping SSH (PID:$SSH_PID)" kill -TERM $SSH_PID wait $SSH_PID 2>/dev/null log_success "SSH stopped" fi log_info "Container shutdown complete" exit 0 } trap cleanup SIGTERM SIGINT # --- Main execution --- main() { setup_logging log_info "===== Container Starting =====" log_info "Time: $(date)" log_info "Timezone: $TZ" log_info "User: $(whoami)" if ! render_config; then log_error "Configuration rendering failed" exit 1 fi if ! /usr/sbin/sshd -t; then log_error "SSH configuration test failed" exit 1 fi if ! nginx -t; then log_error "Nginx configuration test failed" exit 1 fi if ! start_services; then exit 1 fi log_success "===== All services started successfully =====" monitor_services } main EOF RUN chmod +x /app/start.sh # ---- Stage 2: Production ---- # This stage builds the final runtime image with only necessary dependencies FROM ubuntu:22.04 AS production ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Asia/Shanghai ENV PUID=1000 ENV PGID=1000 ENV LANG=zh_CN.UTF-8 # Copy timezone and locale settings from builder COPY --from=builder /etc/localtime /etc/localtime COPY --from=builder /etc/timezone /etc/timezone COPY --from=builder /usr/lib/locale/zh_CN.utf8 /usr/lib/locale/zh_CN.utf8/ COPY --from=builder /etc/default/locale /etc/default/locale # Install only runtime dependencies RUN apt-get update && \ apt-get install -y --no-install-recommends \ openssh-server \ git \ nginx-full \ gettext-base \ curl && \ rm -rf /var/lib/apt/lists/* # Create hexo user RUN groupadd -r hexo && \ useradd -r -g hexo -d /home/hexo -s /bin/bash hexo # Create necessary directories RUN mkdir -p /root/.ssh && \ mkdir -p /home/www/hexo && \ mkdir -p /home/www/ssl && \ mkdir -p /var/run/sshd && \ mkdir -p /var/log/container && \ mkdir -p /var/log/nginx && \ mkdir -p /home/hexo/.ssh # Copy artifacts from builder stage COPY --from=builder /root/hexo.git /root/hexo.git COPY --from=builder /etc/container/templates /etc/container/templates/ COPY --from=builder /app/start.sh /root/start.sh COPY --from=builder /etc/nginx/nginx.conf.bak /etc/nginx/nginx.conf.bak # Set proper permissions RUN chmod +x /root/start.sh && \ chmod +x /root/hexo.git/hooks/post-receive && \ chown -R hexo:hexo /home/www/hexo && \ chown -R hexo:hexo /home/hexo && \ chmod -R 755 /home/www/hexo && \ chmod 700 /root/.ssh && \ chmod 700 /home/hexo/.ssh # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost/ || exit 1 VOLUME ["/home/www/hexo", "/root/.ssh", "/home/www/ssl", "/root/hexo.git", "/var/log/container", "/var/log/nginx"] EXPOSE 22 80 443 CMD ["/root/start.sh"]