geerlingguy / raspberry-pi-dramble

DEPRECATED - Raspberry Pi Kubernetes cluster that runs HA/HP Drupal 8
http://www.pidramble.com/
MIT License
1.67k stars 260 forks source link

Build instructions for how to bootstrap and maintain Drupal 8 codebase + container #111

Closed geerlingguy closed 5 years ago

geerlingguy commented 6 years ago

So here's where a lot of the rubber meets the road...

  1. In #104, I made things work out of the box with a freshly-downloaded-if-not-present Drupal 8 codebase using the geerlingguy/drupal image from Docker Hub.
  2. In #108, I'm working on adding a private Docker registry to the cluster.
  3. There are tons of ways of bootstrapping a new Drupal codebase for a new Drupal site; I like either tarball download or drupal-project if I need more modules and Composery delights.
  4. Very little documentation exists around the web of an actual, "go-to-prod" container build workflow where you have a Drupal codebase, and production (and/or other environments), and you build a container with all the code inside, and you push it to production.

99.9% of all the blog posts, docs, etc., assume you're just mounting the codebase inside the container using a Docker volume / bind mount / etc.

But I want to document (and start using in my personal infra, quite frankly!) a process whereby:

  1. Every commit to master builds a new production-ready Docker container image.
  2. The container image can be automagically deployed up to the production environment using Kubernetes (update the spec.template.spec.containers[0].image in the drupal8.yml manifest to point to the new tagged image or latest hash).

I might not go the full distance and automate everything 100% inside this project's codebase; but I do want that workflow to be enabled easily, and I want at least one or more of the following:

geerlingguy commented 6 years ago

One quick question I want to jot here while it's on my mind:

Is it possible (if so, how?) to make a truly cumalative git-repo-based container image where new tags/hashes/versions only contain a new layer with the changes from the latest git hash (rather than each new container image containing the entire codebase as a layer, meaning every image will be at least as big as the size of the codebase)?

I know that was one of the whiz-bang intro-to-Docker-101 level things I remember hearing about... but in practice most places seem to add a COPY [codebase] [container path] and that generates a new image with a unique layer with the entire codebase.

For a lot of poorly-architected codebases (I've seen them in the 1-5 GB range!), that means giant blobs of images for every single commit, ever!

geerlingguy commented 6 years ago

108 is complete, so we have a local private Docker registry with which images can be managed locally.

geerlingguy commented 6 years ago

The main thing that's annoying—if you do a kubectl delete pod [drupal8 pod here], it brings a new one up, and every time that happens you have to re-run the Drupal installer so the settings.php file knows the database credentials.

Would like to make it so the settings.php can be dynamically generated, or gets info from environment variables (the latter seems better, and the vars for DB connection are already there).

geerlingguy commented 5 years ago

Working on https://github.com/geerlingguy/drupal-container/issues/12 as a stop-gap, to hopefully allow multiple replicas of the Drupal container to run and use the same database connection and not do... weird things.

geerlingguy commented 5 years ago

I think the approach I'm going to take is:

Also going to move #133 into that project's repo, since it literally contains all the contents of the Pi Dramble website now (yay!). And soon I'll also need to install that site on the running cluster/Pi itself and finally step away from Bartik :)

geerlingguy commented 5 years ago

Just a scratchpad for potential changes to make the Drupal Example for Kubernetes to work as a drop-in swap for the current container:

DRUPAL_DATABASE_HOST: 'mysql'
DRUPAL_DATABASE_PORT: '3306'
DRUPAL_DATABASE_NAME: 'drupal'
DRUPAL_DATABASE_USERNAME: 'drupal'
DRUPAL_HASH_SALT: '{{ drupal_hash_salt }}'
DRUPAL_DATABASE_PASSWORD: [secret]

drupal-files NFS volume mount is at path /var/www/html/sites/default/files—might need to make that a variable so I can set it to /var/www/html/web/sites/default/files instead. (Edit: Added var drupal_files_dir).

I also will need to make the FROM allow for an ARG (https://www.jeffgeerling.com/blog/2017/use-arg-dockerfile-dynamic-image-specification), so I can use the base image geerlingguy/drupal:latest-arm32v7 when building for ARM.

geerlingguy commented 5 years ago

To get the registry working correctly, I'm going to need to knock out https://github.com/geerlingguy/raspberry-pi-dramble/issues/114 as well...

geerlingguy commented 5 years ago

Registry is working correctly now. So next steps (jotting since I'm folding up shop for the night):

geerlingguy commented 5 years ago

Looks like I'll have to do a few more things to the Drupal for Kubernetes project to get it fully ready to run in the Kubernetes environment... getting:

[Sun Feb 17 19:09:39.644065 2019] [php7:notice] [pid 10] [client 10.244.2.0:45648] Drupal\\Core\\Database\\DatabaseAccessDeniedException: SQLSTATE[HY000] [1045] Access denied for user 'drupal'@'10.244.1.9' (using password: YES) in /var/www/html/web/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php on line 427 #0 /var/www/html/web/core/lib/Drupal/Core/Database/Database.php(371): Drupal\\Core\\Database\\Driver\\mysql\\Connection::open(Array)\n#1 /var/www/html/web/core/lib/Drupal/Core/Database/Database.php(166): Drupal\\Core\\Database\\Database::openConnection('default', 'default')\n#2 [internal function]: Drupal\\Core\\Database\\Database::getConnection('default')\n#3 /var/www/html/web/core/lib/Drupal/Component/DependencyInjection/PhpArrayContainer.php(79): call_user_func_array('Drupal\\\\Core\\\\Dat...', Array)\n#4 /var/www/html/web/core/lib/Drupal/Component/DependencyInjection/Container.php(171): Drupal\\Component\\DependencyInjection\\PhpArrayContainer->createService(Array, 'database')\n#5 /var/www/html/web/core/lib/Drupal/Component/DependencyInjection/PhpArrayContainer.php(260): Drupal\\Component\\DependencyInjection\\Container->get('database', 1)\n#6 /var/www/html/web/core/lib/Drupal/Component/DependencyInjection/PhpArrayContainer.php(62): Drupal\\Component\\DependencyInjection\\PhpArrayContainer->resolveServicesAndParameters(Array)\n#7 /var/www/html/web/core/lib/Drupal/Component/DependencyInjection/Container.php(171): Drupal\\Component\\DependencyInjection\\PhpArrayContainer->createService(Array, 'cache.container')\n#8 /var/www/html/web/core/lib/Drupal/Core/DrupalKernel.php(543): Drupal\\Component\\DependencyInjection\\Container->get('cache.container')\n#9 /var/www/html/web/core/lib/Drupal/Core/DrupalKernel.php(904): Drupal\\Core\\DrupalKernel->getCachedContainerDefinition()\n#10 /var/www/html/web/core/lib/Drupal/Core/DrupalKernel.php(476): Drupal\\Core\\DrupalKernel->initializeContainer()\n#11 /var/www/html/web/core/lib/Drupal/Core/DrupalKernel.php(692): Drupal\\Core\\DrupalKernel->boot()\n#12 /var/www/html/web/index.php(19): Drupal\\Core\\DrupalKernel->handle(Object(Symfony\\Component\\HttpFoundation\\Request))\n#13 {main}
geerlingguy commented 5 years ago

I had to manually edit settings.php:

$config_directories['sync'] = '../config/sync';
$databases['default']['default'] = array (
  'database' => getenv('DRUPAL_DATABASE_NAME'),
  'username' => getenv('DRUPAL_DATABASE_USERNAME'),
  'password' => getenv('DRUPAL_DATABASE_PASSWORD'),
  'prefix' => '',
  'host' => getenv('DRUPAL_DATABASE_HOST'),
  'port' => '',
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
);
$settings['hash_salt'] = getenv('DRUPAL_HASH_SALT');

Then I had to drop all tables and reinstall Drupal:

$ vendor/bin/drush sql-drop -y
$ vendor/bin/drush site:install minimal --db-url="mysql://drupal:$DRUPAL_DATABASE_PASSWORD@$DRUPAL_DATABASE_HOST/drupal" --site-name="Drupal Example Site for Kubernetes" --existing-config -y
geerlingguy commented 5 years ago

Milestone: I have Drupal for Kubernetes running on the cluster:

site-on-local-cluster

But I need to fix the settings.php situation so I can install-if-not-installed and have it automatically start if already installed (without messing up settings or having the wrong settings).

I thought I had already figured this out elsewhere, but I'm not seeing where I did so :/

geerlingguy commented 5 years ago

Ah... I did set this up over in the drupal-container repo: https://github.com/geerlingguy/drupal-container/blob/master/docker-entrypoint.sh#L27-L42

Problem is, that's only run for the downloaded Drupal tarball, not for the Drupal for Kubernetes codebase! So I'll need to create a template settings.php or something like that for use in production.

geerlingguy commented 5 years ago

Fixed over in https://github.com/geerlingguy/drupal-for-kubernetes/issues/10 — local testing is good so far!

Next up, I need to start testing this on the real Pi cluster... and test building the image on ARM32 (right now I've only tested on AMD64).

geerlingguy commented 5 years ago

Installing on a freshly-reset set of Pis now. Apparently Docker 18.09.2 has not yet been released for Raspbian:

E: Version '5:18.09.2~3-0~raspbian-stretch' for 'docker-ce' was not found

So I have to stick to 5:18.09.0~3-0~raspbian-stretch for now :-/

(See: https://download.docker.com/linux/raspbian/dists/stretch/stable/binary-armhf/Packages)

geerlingguy commented 5 years ago

Weird, now I'm running into:

$ kubectl exec -n drupal $DRUPAL_POD -- bash -c 'vendor/
bin/drush site:install minimal --db-url="mysql://drupal:$DRUPAL_DATABASE_PASSWORD@$DRUPAL_DATABASE_HOST/drupal" --site-name="Drupal Example Site for Kubernetes" --existing-config -y'

 // You are about to DROP all tables in your 'drupal' database. Do you want to  
 // continue?: yes.                                                             

 [notice] Starting Drupal installation. This takes a while.
TypeError: Return value of Doctrine\Common\Annotations\AnnotationRegistry::reset() must be an instance of Doctrine\Common\Annotations\void, none returned in Doctrine\Common\Annotations\AnnotationRegistry::reset() (line 55 of /var/www/html/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php).
TypeError: Return value of Doctrine\Common\Annotations\AnnotationRegistry::reset() must be an instance of Doctrine\Common\Annotations\void, none returned in /var/www/html/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php on line 55 #0 /var/www/html/web/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php(113): Doctrine\Common\Annotations\AnnotationRegistry::reset()
#1 /var/www/html/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php(106): Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery->getDefinitions()
#2 /var/www/html/web/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php(175): Drupal\Core\Entity\EntityTypeManager->findDefinitions()
#3 /var/www/html/web/core/lib/Drupal/Core/Entity/EntityManager.php(675): Drupal\Core\Plugin\DefaultPluginManager->getDefinitions()
#4 /var/www/html/web/core/lib/Drupal/Core/Config/ConfigManager.php(100): Drupal\Core\Entity\EntityManager->getDefinitions()
#5 /var/www/html/web/core/lib/Drupal/Core/Config/ConfigInstaller.php(317): Drupal\Core\Config\ConfigManager->getEntityTypeIdByName('core.extension')
#6 /var/www/html/web/core/lib/Drupal/Core/Config/ConfigInstaller.php(132): Drupal\Core\Config\ConfigInstaller->createConfiguration('', Array)
...
geerlingguy commented 5 years ago

Dangit... apparently this has to do with the PHP 7.0.33 version that is present on the ARM version of geerlingguy/drupal.

I'll have to look at upgrading to 7.1 or later. See: https://github.com/acquia/blt/issues/2660#issuecomment-416316993

Trying https://getgrav.org/blog/raspberrypi-nginx-php7-dev — but actually considering building/using a buster container as it already has php7.2-* packages available and I wouldn't have to twiddle with apt priorities.

geerlingguy commented 5 years ago

Could also use one of my favorite repos—apparently Oerdnj supports ARM now, who knew? https://github.com/oerdnj/deb.sury.org/issues/579

geerlingguy commented 5 years ago

Upstream issue: https://github.com/geerlingguy/drupal-container/issues/17

geerlingguy commented 5 years ago

PHP 7.3.x/buster did not work out of the box, so trying Ondrej Sury's repos now.

geerlingguy commented 5 years ago

Still working on these issues through the drupal-for-kubernetes and drupal-container projects...

geerlingguy commented 5 years ago

Build pipelines are always amazing until there are little failures in multiple stages.

geerlingguy commented 5 years ago

Fixed the upstream issues, running PHP 7.2.x now, and the site loads from the Pi Cluster just as well as it does from the local Vagrant/VirtualBox cluster, yay!

geerlingguy commented 5 years ago

I've added all the build directions to the Drupal for Kubernetes repo over here: https://github.com/geerlingguy/drupal-for-kubernetes/tree/master/docs