bitpressio / docker-for-php-developers-errata

Reported errata for https://bitpress.io/docker-for-php-developers/
13 stars 0 forks source link

docker-laravel-starter-master #14

Open shaneparsons opened 4 years ago

shaneparsons commented 4 years ago

I recently purchased the Starter Book Bundle and was just wondering if the docker-laravel-starter-master is fine to use in production environments? If not, what should be changed to make it fine?

paulredmond commented 4 years ago

Hi @shaneparsons, this is fine to use in production, however, the PHP-FPM configuration isn't optimized for dynamic child processes yet. By default the PHP-FPM config doesn't spin up a ton of child processes, but here's some details:

https://gist.github.com/paulredmond/6e112017d9c934b95617ce7407ba36df

I plan on adding videos for this in my course and updating the starter book bundle with an appendix or something that goes into detail of optimizing PHP-FPM.

If you take the above www.conf pool config and replace it with the default that ships with the PHP image the book is extending you can then make the www pool processes dynamic on any size workload you use with Docker.

shaneparsons commented 4 years ago

Hi @paulredmond, thanks for the quick response!

In the laravel sample there is a conf at docker/php/php-fpm.d/docker.conf:

[global]
daemonize = no
pid = run/php-fpm.pid

[www]
listen = /usr/local/var/run/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

Is the www.conf from the gist being added in addition to this (e.g. docker/php/php-fpm.d/www.conf)? Or is it replacing the[www] section of the above file?

Sorry, most of this is very new to me.


EDIT: Nevermind, I see the revision you made specific to this case. Thanks!

paulredmond commented 4 years ago

Ya I know it’s confusing, I’m happy to help further as needed. I will be formalizing this so it makes more sense in the context of the course too. Thanks for the great feedback

shaneparsons commented 4 years ago

I tried getting this up and running for most of the day yesterday and kept running into problems. In the end, I finally got an environment up and running using DigitalOcean's guide; but it's obvious that yours has a lot more work put into it, as well as other components I need like Redis and Supervisord, so I'd like to use it if I can.

I'll try to sum up the main issues in a readable way... apologies in advance if it's hard to follow along:

  1. The following step of docker-compose up --build -d
    Step 3/19 : RUN apt-get update     && apt-get -y --no-install-recommends install         procps         supervisor         sqlite3         nginx         git         zip         unzip         nano     && docker-php-ext-install mbstring pdo pdo_mysql opcache     && pecl install apcu     && docker-php-ext-enable apcu

    would often hang at:

    Get:9 http://security-cdn.debian.org/debian-security buster/updates/main amd64 Packages [96.2 kB]

    Note: I added git, zip, unzip and nano to the command to avoid having to install composer on my server, and instead run it from the container. More details on the composer issue below.

Note: I'll likely have to do something similar with node / npm as well, as I don't want those on my base server if possible.

  1. Another area that would often hang is at the very end

    Step 17/19 : RUN chown -R www-data:www-data /var/www/html
    ---> Running in 2dfdc4486f49

    Note: this didn't seem to be a problem if the vendor file wasn't built locally... Is it perhaps traversing through it in some way?

  2. Trying to add a domain, among other production stuff, proved troublesome although it very well could have been due to one of the other areas failing... Here's what I tried:

# .env

APP_ENV=production # local
APP_URL=http://mydomain.com # http://localhost
...
DB_HOST=laravel-mysql # 127.0.0.1
...
REDIS_HOST=laravel-redis # 127.0.0.1
# Dockerfile

# NGINX_SERVER_NAME="_" \
NGINX_SERVER_NAME="mydomain.com" \
# docker-compose.yml

environment:
      APP_ENV: production # local

In the end the domain just wouldn't resolve to anything, and I wasn't able to see the Laravel site.

  1. Re-running docker-compose up --build -d every time I ran into an issue ate up a lot of time, almost as if it was building everything from scratch each time. Is there a better command to run in a case where I only make a small change to the Dockerfile? I know you touched on the subject in your book, but it was in a slightly different context.

I'm probably forgetting something, but those are the main points I remember. Do you see anything that stands out as an issue with any of the steps I've taken? Also, do you see any problem with DigitalOceans's guide in comparison to yours? There seem to be a lot of differences between the two.

If I am able to get this to work my final question, and perhaps the question that started me down this entire path to begin with, is how to then integrate this Laravel site with traefik or jwilder/nginx-proxy, so that I can serve it alongside other sites (Laravel and Non-Laravel). For the time being we'll leave this aside, but I figured it might you help to further understand my vision for this server.

Sorry again for the length of this.

paulredmond commented 4 years ago

@shaneparsons I'll do my best to respond to this 😄

First, the official node docker images are great, and include both NPM and Yarn.

You very well might want to build your own base image with composer for PHP projects and whatever other tools you want on most projects. Extending a base image will also help with build times on your projects and give you a base for your projects to work from.

Performance

Second, you mentioned performance with the chown code during a build. I agree with you that it's slow. On a Laravel project, the following would be enough instead of all files:

RUN ...
&& chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache

I have also seen builds go slow because of apt-get update. I'd recommend creating a base image that you can extend in your projects to avoid having to build the full image every time for a project. You will want to update the base build from time-to-time to get the latest patches and then update your image that extends your base image.

Nginx

Typically I am running Docker containers behind a load balancer, talking on port 80. You can indeed set the NGINX_SERVER_NAME with the starter laravel project. That should be working as long as your container running is mapped to port 80 on the server, it should respond. I'd check the server to see if the container is running and peek around to see what the nginx config looks like in the running container. I'd also try to make a request from the server:

curl -i -H"Host: example.com" http://localhost/my/url

Environment

I'd recommend setting all .env values through environment variables. If you want a .env file, I'd recommend creating a .env.production file in the project and copying it into the container as .env during the docker build process. Avoid secrets, but you can use the .env.production file to configure non-sensitive .env values In the Dockerfile like so:

# Dockerfile
# ...
RUN mv .env.production .env

Your local .env file will override it locally via the volume, so no worries in this file existing. I'd also recommend generating the app key during a build if you go this route:

# Dockerfile
# ...
RUN mv .env.production .env && php artisan key:generate

Again, I prefer to set most/all config via environment variables in production, so if you are using docker run to run the container on digital ocean you might want to research a good way to set secrets as environment variables in a secure way.

I realize that running Docker in production can get confusing and complicated, so I hope this helps!

shaneparsons commented 4 years ago

Thanks @paulredmond, lots of good advice here! I appreciate the time you've put into all of this.

shaneparsons commented 4 years ago

Hi @paulredmond,

I got sidetracked onto a few other projects, but I've finally made my way back to this Laravel stuff. I've gone through / tried implementing your comments above numerous times, but there's still something wrong with the setup / some stuff I'm not fully understanding.

Is there any chance that you intend on adding production steps / examples to these starter files in the near future? The readme.md and all the comments you've put throughout the files make it extremely easy to get up and running / understand in a dev environment, I just wish it was as straight forward for the production environment. A working / best practices setup with production versions of .dockerfile, docker-compose.yml, readme.md and the .env files would help immensely, as I'd finally be able to understand where I'm going wrong... Rather than me poking around the dev examples trying to turn it into production myself.

Let me know your thoughts!

paulredmond commented 4 years ago

Hi @shaneparsons thanks for all the wonderful feedback. I need to give the starter code some attention, and I will reach out to you personally for some feedback. I am not sure on timing, but for sure I plan on addressing your concerns, thanks! I'll keep this ticket updated.

shaneparsons commented 4 years ago

@paulredmond thanks man, I really appreciate it!

In the meantime, I'm curious if you might be able to shed some light on my current situation? I believe all the containers are working properly, with exception to the nginx container... more specifically the conf.d part of it.

I'm getting a 502 / Bad Gateway error when trying to navigate to the url I set for the container... When looking into the logs, I see this (trimmed):

2019/10/31 17:49:06 [crit] 31#31: *1 connect() to unix:/usr/local/var/run/php-fpm.sock failed (2: No such file or directory) while connecting to upstream"

I don't believe I modified anything to do with php-fpm, so I'm having trouble tracking down what's going wrong. Any ideas why that file isn't there?


EDIT: The only changes I made to php-fpm are the ones suggested earlier in this thread:

[global]
daemonize = no
pid = run/php-fpm.pid

[www]
listen = /usr/local/var/run/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

# php-fpm revisions - see: https://gist.github.com/paulredmond/6e112017d9c934b95617ce7407ba36df
pm = dynamic
pm.max_children = ${PHP_FPM_PM_MAX_CHILDREN}
pm.start_servers = ${PHP_FPM_PM_START_SERVERS}
pm.min_spare_servers = ${PHP_FPM_PM_MIN_SPARE_SERVERS}
pm.max_spare_servers = ${PHP_FPM_PM_MAX_SPARE_SERVERS}
pm.max_requests = ${PHP_FPM_PM_MAX_REQUESTS}
PHP_FPM_PM_MAX_CHILDREN=5 \
PHP_FPM_PM_START_SERVERS=2 \
PHP_FPM_PM_MIN_SPARE_SERVERS=1 \
PHP_FPM_PM_MAX_SPARE_SERVERS=3 \
PHP_FPM_PM_MAX_REQUESTS=500

EDIT 2: Fuck me... the problem was that damn # comment I had in docker/php/php-fpm.d/docker.conf. I'm now getting 500 errors, but at least I'm one step closer now! I'll do some more investigating tomorrow.

paulredmond commented 4 years ago

Sounds good, hope it goes well! 🤞

shaneparsons commented 4 years ago

I've tracked the issue down to this line at the bottom of the Dockerfile:

RUN chown -R www-data:www-data /var/www/html

I'm not sure what the best solution is... but the permissions aren't being set properly...

The stream or file "/var/www/html/storage/logs/laravel-2019-11-01.log" could not be opened: failed to open stream: Permission denied

Per some post I found on github I tried running chown -R www-data: /var/www/html from within the app container, and while it did fix my issue, all my files outside of the container no longer gave me permisson to edit / save them.

How can I modify the inital chown command above to properly set the permissions? This command (posted in a previous reply above) doesn't do it either for some reason:

RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
shaneparsons commented 4 years ago

I finally got past the permission issues, and the others that followed... but it involved some re-configuring:

I had to move chown command to run-app.sh... Not entirely sure why, but I think something in this file was generating the log file with the bad permissions, hence why I change permissions near the end of the file:

...
if [ "$role" = "app" ]; then
    chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
...

I then ran into an issue that seems to have been around for a long time with Laravel... Basically, if any of the routes have a closure eg:

Route::get('/{any}', function () {
    return view('app');
})->where('any', '.*');

you are unable to run php artisan route:cache... So I removed that part from the artisan command in run-app.sh. eg:

    cd /var/www/html && php artisan config:cache
shaneparsons commented 4 years ago

I also ran into issues with Laravel using old versions of my js files, regardless of how many cache clearing things I tried... I was able to get around this by using versioning in my webpack.mix.js though:

const mix = require('laravel-mix');

mix
  .js('resources/js/app.js', 'public/js')
  .js('resources/js/api.js', 'public/js')
  .sass('resources/sass/app.scss', 'public/css')
  .copyDirectory('resources/img', 'public/img');

if (mix.inProduction()) {
  mix.version();
}

I'm not sure if this particular issue had anything to do with this docker setup, but I don't remember running into it when hosting Laravel in the traditional way so I figured I'd share it here.