Kubernetes环境下部署Rails+Unicorn+Nginx应用无法加载静态资源
Hey there, let's dig into why your Rails app's static assets aren't loading on Kubernetes—this is a super common gotcha when moving from Docker Compose to K8s, so let's break down targeted fixes and checks:
1. First, Confirm Rails Asset Precompilation is Working
Rails doesn't serve static assets in production mode by default—you need to precompile them first, and make sure that step is baked into your Docker image.
- Double-check your Dockerfile includes this command (after installing gems and copying app code):
RUN RAILS_ENV=production bundle exec rails assets:precompile - Verify your Deployment sets the production environment via env vars:
env: - name: RAILS_ENV value: "production" - You can also exec into a running pod to check if assets exist:
You should see hashed files likekubectl exec -it <your-app-pod-name> -- ls public/assetsapplication-abc123.css—if not, precompilation failed.
2. Make Sure Your Web Server is Serving Static Assets
Your choice of server (Unicorn, Passenger, Nginx) dictates how assets are handled:
- Unicorn: By default, Unicorn won't serve static assets. You can either:
- Enable it temporarily in
config/environments/production.rb(not ideal for performance):config.serve_static_assets = true # Or for Rails 5+, use: config.public_file_server.enabled = true - Or add an Nginx sidecar or combine Nginx with Unicorn in your image to serve assets directly (this is the production-grade approach).
- Enable it temporarily in
- Combined Rails + Nginx Image: If you went this route, ensure your Nginx config has a block to serve assets:
location ~ ^/assets/ { root /app/public; expires 1y; add_header Cache-Control public; add_header ETag ""; } - Passenger: It should handle static assets automatically, but confirm
config.public_file_server.enabled = trueis set in your production config.
3. Tweak Your Ingress Configuration (The Likely Culprit)
Your existing tweaks might have missed a few key details:
- Ditch the
ingress.kubernetes.io/add-base-urlannotation unless your app is hosted under a subpath (likeyourdomain.com/appx). This annotation can rewrite asset paths incorrectly. - Use
pathType: Prefixfor your root path to ensure all requests (including assets) are forwarded to your service:spec: rules: - host: your-app-domain.com http: paths: - path: / pathType: Prefix backend: service: name: appX-cluster-ip-service port: number: 80 - Check your ingress controller logs for 404s related to assets:
kubectl logs -n kube-system <your-ingress-controller-pod> | grep assets
4. Debug Pod-to-Service Connectivity
Rule out issues inside your cluster:
- Exec into your app pod and test if assets load locally:
If this returns the asset content, the problem is in your Ingress or Service. If not, the issue is inside the pod (web server config or missing assets).kubectl exec -it <your-app-pod> -- curl localhost:<container-port>/assets/application-<hash>.css - Confirm your Service is targeting the correct container port (e.g., Unicorn usually listens on 8080, Nginx on 80).
5. Use Browser Dev Tools to Diagnose Request Issues
Open your browser's dev tools (F12) and check the Network tab:
- Look for asset requests with 404/403 status codes. If the URL is something like
yourdomain.com/assets/application.css(no hash), that means Rails isn't generating the hashed asset path correctly—double-check precompilation andRAILS_ENV. - If the URL has the hash but returns 404, your server isn't routing the request to the correct asset file.
Quick Stable Setup Example (Rails + Unicorn + Nginx)
If you want a reliable baseline, here's a simplified Dockerfile to combine both servers:
FROM ruby:2.5.5-slim # Install system dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential nginx \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # Install gems COPY Gemfile Gemfile.lock ./ RUN bundle install --without development test --deployment # Copy app code COPY . . # Precompile assets RUN RAILS_ENV=production bundle exec rails assets:precompile # Copy Nginx config COPY nginx.conf /etc/nginx/nginx.conf # Start both servers CMD ["bash", "-c", "bundle exec unicorn -c config/unicorn.rb & nginx -g 'daemon off;'"]
And a basic nginx.conf:
events { worker_connections 1024; } http { server { listen 80; root /app/public; # Serve static assets directly location ~ ^/assets/ { expires 1y; add_header Cache-Control public; } # Forward dynamic requests to Unicorn location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } }
内容的提问来源于stack exchange,提问作者Alejandro Roble Gutiérrez

