Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Need help for migrating to nginx buildpack #245

Open
kanadgodse-lh opened this issue Jun 16, 2022 · 13 comments
Open

Need help for migrating to nginx buildpack #245

kanadgodse-lh opened this issue Jun 16, 2022 · 13 comments

Comments

@kanadgodse-lh
Copy link

Hello,

We have many production react apps and use this buildpack along with https://github.com/mars/create-react-app-buildpack

Since that buildpack and this one is also deprecated and the migration guide does not give details for the following step, I am stuck:
Replace path logic that previously used mruby with static logic.

Can someone please help by elaborating how to replace path logic with static logic?

@edmorley
Copy link
Member

@heroku/languages Could someone take a look at this? Making it easier for people to migrate from the static buildpack will help with Heroku-22 adoption, and thus also with Heroku-20 EOL (when that time comes) :-)

@tylerjgarland
Copy link

@kanadgodse-lh , if you cat the ruby files in question

~ $ cat /app/bin/config/lib/ngx_mruby/headers.rb
# ghetto require, since mruby doesn't have require
eval(File.read('/app/bin/config/lib/nginx_config_util.rb'))

USER_CONFIG = "/app/static.json"

config = {}
config = JSON.parse(File.read(USER_CONFIG)) if File.exist?(USER_CONFIG)
req    = Nginx::Request.new
uri    = req.var.uri

if config["headers"]
  config["headers"].to_a.reverse.each do |route, header_hash|
    if Regexp.compile("^#{NginxConfigUtil.to_regex(route)}$") =~ uri
      header_hash.each do |key, value|
        # value must be a string
        req.headers_out[key] = value.to_s
      end
      break
    end
  end
end
~ $ cat /app/bin/config/lib/ngx_mruby/routes_fallback.rb
# ghetto require, since mruby doesn't have require
eval(File.read('/app/bin/config/lib/nginx_config_util.rb'))

USER_CONFIG = "/app/static.json"

config    = {}
config    = JSON.parse(File.read(USER_CONFIG)) if File.exist?(USER_CONFIG)
req       = Nginx::Request.new
uri       = req.var.uri
proxies   = config["proxies"] || {}
redirects = config["redirects"] || {}

if proxy = NginxConfigUtil.match_proxies(proxies.keys, uri)
  "@#{proxy}"
elsif redirect = NginxConfigUtil.match_redirects(redirects.keys, uri)
  "@#{redirect}"
else
  "@404"
end

It seems to mostly be processing your cache-control headers you put in your static.json file. I just removed the mruby lines from the nginx config file and dropped the $fallback variable from the try_files call.

@jamesmichiemo
Copy link

@kanadgodse-lh , if you cat the ruby files in question

~ $ cat /app/bin/config/lib/ngx_mruby/headers.rb
# ghetto require, since mruby doesn't have require
eval(File.read('/app/bin/config/lib/nginx_config_util.rb'))

USER_CONFIG = "/app/static.json"

config = {}
config = JSON.parse(File.read(USER_CONFIG)) if File.exist?(USER_CONFIG)
req    = Nginx::Request.new
uri    = req.var.uri

if config["headers"]
  config["headers"].to_a.reverse.each do |route, header_hash|
    if Regexp.compile("^#{NginxConfigUtil.to_regex(route)}$") =~ uri
      header_hash.each do |key, value|
        # value must be a string
        req.headers_out[key] = value.to_s
      end
      break
    end
  end
end
~ $ cat /app/bin/config/lib/ngx_mruby/routes_fallback.rb
# ghetto require, since mruby doesn't have require
eval(File.read('/app/bin/config/lib/nginx_config_util.rb'))

USER_CONFIG = "/app/static.json"

config    = {}
config    = JSON.parse(File.read(USER_CONFIG)) if File.exist?(USER_CONFIG)
req       = Nginx::Request.new
uri       = req.var.uri
proxies   = config["proxies"] || {}
redirects = config["redirects"] || {}

if proxy = NginxConfigUtil.match_proxies(proxies.keys, uri)
  "@#{proxy}"
elsif redirect = NginxConfigUtil.match_redirects(redirects.keys, uri)
  "@#{redirect}"
else
  "@404"
end

It seems to mostly be processing your cache-control headers you put in your static.json file. I just removed the mruby lines from the nginx config file and dropped the $fallback variable from the try_files call.

So I removed every line with mruby and every $fallback variable from my config/nginx.conf.erb that was created from using

$ heroku run bash
~ $ bin/config/make-config
~ $ cat config/nginx.conf

This was from inside the app that I removed the create-react-app-buildpack from and replaced with heroku-community/nginx. Somehow I got a build succeed but it's not rendering anything for me.

@tylerjgarland
Copy link

As a test @jamesmichiemo , can you slap a /index.html on the end of your URL?

@jamesmichiemo
Copy link

As a test @jamesmichiemo , can you slap a /index.html on the end of your URL?

Thank you @tylerjgarland for assisting! I'll try that change eventually but it feels like it would be worth more of my time if I tried the other alternative hosting providers listed on the create-react-app deployment docs and maybe telling them to remove Heroku from their documentation for now until the chaos dies.

@xiaopow
Copy link

xiaopow commented Jul 15, 2022

Hi. I tried to follow the instructions regarding the mruby static logic.

I added node.js and nginx buildpack.

Screenshot 2022-07-15 at 2 47 33 PM

Deployed. The node build succeeded. The nginx didn't give any errors.

But when I visited the app https://invulnerable-maison-61218.herokuapp.com/ there is an app error.

In the server log is this.

2022-07-15T06:45:42.939892+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/" host=invulnerable-maison-61218.herokuapp.com request_id=5ad9be69-bdfb-4670-b73b-77d4ed89506f fwd="221.124.121.128" dyno= connect= service= status=503 bytes= protocol=https
2022-07-15T06:45:43.797328+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/favicon.ico" host=invulnerable-maison-61218.herokuapp.com request_id=e85c496a-6723-4c4a-9149-8cd07ebde3b7 fwd="221.124.121.128" dyno= connect= service= status=503 bytes= protocol=https
2022-07-15T06:45:47.687092+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/index.html" host=invulnerable-maison-61218.herokuapp.com request_id=469f0537-7db3-43b0-93d3-09b5dedfc325 fwd="221.124.121.128" dyno= connect= service= status=503 bytes= protocol=https
2022-07-15T06:45:48.506074+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/favicon.ico" host=invulnerable-maison-61218.herokuapp.com request_id=9f9ed82a-0a53-4445-9b96-a57ba605ac45 fwd="221.124.121.128" dyno= connect= service= status=503 bytes= protocol=https

Here is my config/nginx.conf.erb file which I generated based on another react app deployed on Heroku using the mars buildpack.

daemon off;
worker_processes auto;

events {
  use epoll;
  accept_mutex on;
  worker_connections 512;
}

http {
  gzip on;
  gzip_comp_level 6;
  gzip_min_length 512;
  gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
  gzip_vary on;
  gzip_proxied any;

  server_tokens off;


  access_log logs/access.log;



  error_log stderr error;


  include mime.types;
  default_type application/octet-stream;
  sendfile on;

  #Must read the body in 5 seconds.
  client_body_timeout 5;

  server {
    listen 38169 reuseport;
    charset UTF-8;
    port_in_redirect off;
    keepalive_timeout 5;
    root build/;






    location / {

      try_files $uri $uri/;

    }








    location ~ ^/.*$ {
      set $route /.*;

      try_files $uri $path ;

    }


  # need this b/c setting $fallback to =404 will try #{root}=404 instead of returning a 404
  location @404 {
    return 404;
  }

  # fallback proxy named match


  # fallback redirects named match


  }
}

@xiaopow
Copy link

xiaopow commented Jul 15, 2022

Okay, I swapped the order of the two buildpacks and the app is working now.
Screenshot 2022-07-15 at 2 55 50 PM

@andersonar12
Copy link

andersonar12 commented Jul 29, 2022

Hi. I tried to follow the instructions regarding the mruby static logic.

I added node.js and nginx buildpack.

Screenshot 2022-07-15 at 2 47 33 PM

Deployed. The node build succeeded. The nginx didn't give any errors.

But when I visited the app https://invulnerable-maison-61218.herokuapp.com/ there is an app error.

In the server log is this.

2022-07-15T06:45:42.939892+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/" host=invulnerable-maison-61218.herokuapp.com request_id=5ad9be69-bdfb-4670-b73b-77d4ed89506f fwd="221.124.121.128" dyno= connect= service= status=503 bytes= protocol=https
2022-07-15T06:45:43.797328+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/favicon.ico" host=invulnerable-maison-61218.herokuapp.com request_id=e85c496a-6723-4c4a-9149-8cd07ebde3b7 fwd="221.124.121.128" dyno= connect= service= status=503 bytes= protocol=https
2022-07-15T06:45:47.687092+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/index.html" host=invulnerable-maison-61218.herokuapp.com request_id=469f0537-7db3-43b0-93d3-09b5dedfc325 fwd="221.124.121.128" dyno= connect= service= status=503 bytes= protocol=https
2022-07-15T06:45:48.506074+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/favicon.ico" host=invulnerable-maison-61218.herokuapp.com request_id=9f9ed82a-0a53-4445-9b96-a57ba605ac45 fwd="221.124.121.128" dyno= connect= service= status=503 bytes= protocol=https

Here is my config/nginx.conf.erb file which I generated based on another react app deployed on Heroku using the mars buildpack.

daemon off;
worker_processes auto;

events {
  use epoll;
  accept_mutex on;
  worker_connections 512;
}

http {
  gzip on;
  gzip_comp_level 6;
  gzip_min_length 512;
  gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
  gzip_vary on;
  gzip_proxied any;

  server_tokens off;


  access_log logs/access.log;



  error_log stderr error;


  include mime.types;
  default_type application/octet-stream;
  sendfile on;

  #Must read the body in 5 seconds.
  client_body_timeout 5;

  server {
    listen 38169 reuseport;
    charset UTF-8;
    port_in_redirect off;
    keepalive_timeout 5;
    root build/;






    location / {

      try_files $uri $uri/;

    }








    location ~ ^/.*$ {
      set $route /.*;

      try_files $uri $path ;

    }


  # need this b/c setting $fallback to =404 will try #{root}=404 instead of returning a 404
  location @404 {
    return 404;
  }

  # fallback proxy named match


  # fallback redirects named match


  }
}

@xiaopow I need to deploy a Vue application on heroku with the new buildpack, I tried this configuration and it didn't work.

@tylerjgarland
Copy link

@andersonar12 , did you create a web procfile?

@loicplaire
Copy link

Unfortunately, none of these examples works for me either, Heroku complains about the missing start script. We use heroku-buildpack-static to deploy a storybook build folder and have no node server running.

What is unclear to me is whether we now need a web Procfile to start a (node) server? Did this buildback handle it before?

Cheers!

@DanAndreasson
Copy link

After much back and forth I finally got the migration working for us. React + vite.
Two things to take notice of

  1. you need to create a Procfile with
web: bin/start-nginx-solo
  1. Need to remove all mentions of mruby in the config file and have to use environment variable for the port.
    This is what I ended up with
daemon off;
worker_processes auto;

events {
  use epoll;
  accept_mutex on;
  worker_connections 512;
}

http {
  gzip on;
  gzip_comp_level 6;
  gzip_min_length 512;
  gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
  gzip_vary on;
  gzip_proxied any;

  server_tokens off;
  access_log logs/access.log;
  error_log stderr error;
  include mime.types;
  default_type application/octet-stream;
  sendfile on;

  # Must read the body in 5 seconds.
  client_body_timeout 5;

  server {
    listen <%= ENV["PORT"] %>;
    charset UTF-8;
    port_in_redirect off;
    keepalive_timeout 5;
    root /app/dist;

    resolver 10.1.0.2 8.8.8.8;

    # Allow payloads of up to 10mb
    client_max_body_size 10M;

    # Redirect all non-https traffic to https
    if ($http_x_forwarded_proto != "https") {
      return 301 https://$host$request_uri;
    }

    location = /index.html {
      add_header Cache-Control "no-store, no-cache";
      add_header Strict-Transport-Security "max-age=31536002;";

      try_files $uri $uri/ =404;
    }

    location / {
      add_header 'Cache-Control' "public, max-age=3600";

      try_files $uri $uri/ /index.html;
    }

    location /manifest.json {
      add_header Cache-Control "no-store, no-cache";
      add_header Access-Control-Allow-Origin "*";

      try_files $uri $uri/ =404;
    }

    # Cache assets for a long time, since all of them get unique names on each build
    location ~ ^/assets/[^/]*$ {
      add_header Cache-Control "public, max-age=31536000";
      add_header Access-Control-Allow-Origin "*";

      try_files $uri $uri/ =404;
    }


    # Need this b/c setting $fallback to =404 will try #{root}=404 instead of returning a 404
    location @404 {
      return 404;
    }

    # Redirect all request on /api/* to backend server
    set $api_endpoint '<%= "#{ENV['VITE_API_PROTOCOL']}://#{ENV['VITE_API_ENDPOINT']}" %>';
    location /api/ {
      rewrite ^/api//?(.*)$ /$1 break;
      proxy_pass $api_endpoint;
      proxy_ssl_server_name on;
      proxy_redirect $api_endpoint/ /api/;
    }
  }
}

This was migrated from static.json

{
  "root": "./dist",
  "https_only": true,

  "headers": {
    "/**": {
      "Strict-Transport-Security": "max-age=7776001"
    },

    "/": {
      "Cache-Control": "no-store, no-cache",
      "Strict-Transport-Security": "max-age=7776002"
    },

    "/assets/**": {
      "Cache-Control": "public, max-age=31536000",
      "Access-Control-Allow-Origin": "*"
    },

    "/manifest.json": {
      "Cache-Control": "no-store, no-cache",
      "Access-Control-Allow-Origin": "*"
    }
  },

  "routes": {
    "/assets/*": "/assets/",
    "/**": "index.html"
  },

  "proxies": {
    "/api/": {
      "origin": "${VITE_API_PROTOCOL}://${VITE_API_ENDPOINT}"
    }
  }
}

@adamalfredsson
Copy link

adamalfredsson commented Oct 22, 2022

After much back and forth I finally got the migration working for us. React + vite. Two things to take notice of

  1. you need to create a Procfile with
web: bin/start-nginx-solo
  1. Need to remove all mentions of mruby in the config file and have to use environment variable for the port.
    This is what I ended up with
daemon off;
worker_processes auto;

events {
  use epoll;
  accept_mutex on;
  worker_connections 512;
}

http {
  gzip on;
  gzip_comp_level 6;
  gzip_min_length 512;
  gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
  gzip_vary on;
  gzip_proxied any;

  server_tokens off;
  access_log logs/access.log;
  error_log stderr error;
  include mime.types;
  default_type application/octet-stream;
  sendfile on;

  # Must read the body in 5 seconds.
  client_body_timeout 5;

  server {
    listen <%= ENV["PORT"] %>;
    charset UTF-8;
    port_in_redirect off;
    keepalive_timeout 5;
    root /app/dist;

    resolver 10.1.0.2 8.8.8.8;

    # Allow payloads of up to 10mb
    client_max_body_size 10M;

    # Redirect all non-https traffic to https
    if ($http_x_forwarded_proto != "https") {
      return 301 https://$host$request_uri;
    }

    location = /index.html {
      add_header Cache-Control "no-store, no-cache";
      add_header Strict-Transport-Security "max-age=31536002;";

      try_files $uri $uri/ =404;
    }

    location / {
      add_header 'Cache-Control' "public, max-age=3600";

      try_files $uri $uri/ /index.html;
    }

    location /manifest.json {
      add_header Cache-Control "no-store, no-cache";
      add_header Access-Control-Allow-Origin "*";

      try_files $uri $uri/ =404;
    }

    # Cache assets for a long time, since all of them get unique names on each build
    location ~ ^/assets/[^/]*$ {
      add_header Cache-Control "public, max-age=31536000";
      add_header Access-Control-Allow-Origin "*";

      try_files $uri $uri/ =404;
    }


    # Need this b/c setting $fallback to =404 will try #{root}=404 instead of returning a 404
    location @404 {
      return 404;
    }

    # Redirect all request on /api/* to backend server
    set $api_endpoint '<%= "#{ENV['VITE_API_PROTOCOL']}://#{ENV['VITE_API_ENDPOINT']}" %>';
    location /api/ {
      rewrite ^/api//?(.*)$ /$1 break;
      proxy_pass $api_endpoint;
      proxy_ssl_server_name on;
      proxy_redirect $api_endpoint/ /api/;
    }
  }
}

This was migrated from static.json

{
  "root": "./dist",
  "https_only": true,

  "headers": {
    "/**": {
      "Strict-Transport-Security": "max-age=7776001"
    },

    "/": {
      "Cache-Control": "no-store, no-cache",
      "Strict-Transport-Security": "max-age=7776002"
    },

    "/assets/**": {
      "Cache-Control": "public, max-age=31536000",
      "Access-Control-Allow-Origin": "*"
    },

    "/manifest.json": {
      "Cache-Control": "no-store, no-cache",
      "Access-Control-Allow-Origin": "*"
    }
  },

  "routes": {
    "/assets/*": "/assets/",
    "/**": "index.html"
  },

  "proxies": {
    "/api/": {
      "origin": "${VITE_API_PROTOCOL}://${VITE_API_ENDPOINT}"
    }
  }
}

This is gold! Couldn't have done this without your help!

@midoh7
Copy link

midoh7 commented Apr 28, 2023

Screenshot 2023-04-28 at 11 29 15

@adamalfredsson I run into the problem recently and after following all the steps here I got 502 bad gateway, don't know what to do next

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants