Skip to content

Commit

Permalink
Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-bouvy committed May 24, 2024
1 parent be973bd commit e53b146
Show file tree
Hide file tree
Showing 8 changed files with 395 additions and 6 deletions.
9 changes: 8 additions & 1 deletion docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@ const config = defineConfig({
{ text: 'Resources and scaling', link: '/guide/deployment/resources-scaling' },
{ text: 'Helm chart', link: '/guide/deployment/helm-chart' },
]
}
},
{
text: '🧩 Advanced',
collapsed: false,
items: [
{ text: 'High availability', link: '/guide/advanced/high-availability' },
]
},
],
editLink: {
pattern: 'https://github.com/ClickAndMortar/magento-kubernetes/edit/main/docs/:path',
Expand Down
5 changes: 5 additions & 0 deletions docs/guide/advanced/high-availability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: High availability
---

# High availability
67 changes: 67 additions & 0 deletions docs/guide/build/optimization.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,70 @@ title: Optimization
---

# Optimization

Our newly built Docker image is ready, but it's not fully optimized for production yet, as it's current size is over 1GB.

We need to make sure that our image is as small as possible, so it can be easily distributed and deployed.

## Reduce number of layers

Each `RUN` instruction in our `Dockerfile` creates a new layer in the image. The more layers we have, the bigger the image will be.

Even if a file is removed in a subsequent layer, it will still be present in the image, as each layer is immutable:

```dockerfile
# Layer 1
RUN echo "Lots of contents [...]" > /tmp/hello.txt

# Layer 2
RUN rm /tmp/hello.txt
```

In the example above, the file `/tmp/hello.txt` will still be present in the image (although not visible), even though it was removed in the second layer.

To reduce the number of layers, we can combine multiple `RUN` instructions into a single one:

```dockerfile
RUN echo "Lots of contents [...]" > /tmp/hello.txt \
# Do something with the file \
&& rm /tmp/hello.txt
```

## Remove useless files

When building our image, we may have installed some packages or copied some files that are not needed in the final image.

For example, our `vendor` directory may contain development files that are not needed in production:

```shell
$ find vendor/ -type d \( -iname 'test' -o -iname 'tests' \) -exec du -s {} + | awk '{sum += $1} END {print sum}'
213112 # Size of the test directories in kilobytes
```

We have over **200MB** of test files in our `vendor` directory. We can remove them by adding the following line to our `Dockerfile`.

As mentionned before, removing them in a new `RUN` instruction will not reduce the size of the image, as the files will still be present in the previous layer.

Hence, we need to remove them in the same layer as the `composer install` command:

```dockerfile
RUN composer install --no-dev --no-interaction --no-progress --no-suggest \
# Remove test directories
&& find vendor/ -type d \( -iname 'test' -o -iname 'tests' \) -exec rm -rf {} +
```

> [!WARNING]
> Be careful when removing files, as you may remove files that are needed in production.
> You should check first the list of directories / files that will be removed before running the command, or even better, remove an explicit list of directories.
## Use a smaller base image

The base image we used, `php:8.3.7-fpm-bookworm`, is around **165MB** in size.

We can use a smaller base image, such as `php:8.3.7-fpm-alpine`, which is around **30MB** in size.

However, using a smaller base image may require some changes to our `Dockerfile`, as some packages may not be available in the Alpine image.

Also note that the Alpine image uses `musl` as its standard C library, while the Debian image uses `glibc`. This may cause some issues with some PHP extensions.

`musl` versions of pre-built extensions should be used, as for New Relic one, or the extensions should be compiled from source.
53 changes: 53 additions & 0 deletions docs/guide/build/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,56 @@ title: Security
---

# Security

## Running containers as an unprivileged user

Although our nginx and PHP FPM workers are running as unprivileged users (respectively `nginx` and `www-data`), our containers are still running as `root`.

This is a security risk, as an attacker who gains access to the container could escalate their privileges to `root` and potentially compromise the host system.

To mitigate this risk, we need to run our containers as an unprivileged user.

To run our containers as an unprivileged user, we need to create a new user and group in our `Dockerfile`, and then switch to that user before running our application.

Let's start by creating a new user, `docker`, and group in our `Dockerfile`, in both the PHP FPM and nginx stages:

```dockerfile
# Create a new group and user
RUN groupadd docker \
&& useradd -m -g docker docker

# Switch to the new user
USER docker
```

> [!NOTE]
> Switching to the new user in the `Dockerfile` is not mandatory, as it could be achieved by setting the `securityContext.runAsUser` property in the Kubernetes `Deployment` manifest.
We'll also need to make a change to our nginx configuration, as the unprivileged user `docker` does not have permission to bind to port `80`:

::: code-group
```nginx [vhost.conf]
server {
listen 80; # [!code --]
listen 8080; # [!code ++]
...
}
```
:::

## Scanning for vulnerabilities

We need to make sure that our image does not contain any known vulnerabilities, by scanning it with a vulnerability scanner.

There are many tools available to scan Docker images for vulnerabilities, exposed secrets and misconfigurations, such as [Trivy](https://aquasecurity.github.io/trivy/).

Trivy also scans for vulnerabilities in your Composer dependencies, which is very useful in our case.

You can run Trivy in your CI/CD pipeline to ensure that your images are free of vulnerabilities before deploying them to production.

## Advanced security features

We will cover advanced security features in dedicated section, such as:

* Read-only file system
* Hardening image
3 changes: 0 additions & 3 deletions docs/guide/deployment/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,6 @@ One `Deployment` needs to be created for each consumer, each containing a single
> At the time of writing, Magento / Adobe Commerce does not handle UNIX signals properly, which means that the containers will not be able to handle graceful shutdowns.
> Therefore, the `Pods` will be terminated immediately, without giving the application time to finish processing the current message.
> [!INFO]
> The `queue/consumers_wait_for_messages` configuration setting in `env.php` must be set to `1` to avoid having the consumers restarting too often.
At the time of writing, the following consumers are available:

* `saveConfigProcessor`
Expand Down
45 changes: 45 additions & 0 deletions docs/guide/deployment/helm-chart.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,48 @@ title: Helm chart
---

# Helm chart

A [Helm](https://helm.sh/) chart is a collection of files that describe a set of Kubernetes resources. It allows you to define, install, and upgrade even the most complex Kubernetes applications.

## Deployment process

When deploying our Magento / Adobe Commerce, the process is as follows:

1. Run the `bin/magento setup:upgrade` command to update the database schema according to the new code and configuration
2. Update all the resources in the Kubernetes cluster with the new version of the application:
* `Deployment`
* `Service`
* `Ingress`
* `ConfigMap`
* `Secret`
* `CronJob`
3. Wait for the new `Pods` to be ready
4. Flush the cache

> [!NOTE]
> When using per release Redis ID prefixes, there is no need to flush the cache after each deployment.
> [!IMPORTANT]
> We will rely on [Helm hooks](https://helm.sh/docs/topics/charts_hooks/) to run the `bin/magento setup:upgrade` in a Kubernetes `Job` (during the `pre-install` and `pre-upgrade` hooks).<br/>
> However, as the updated `ConfigMap` and `Secret` are not updated during the `Job` execution, you may need to declare environment variables directly on the hook `Job` on change, for instance if you change the MySQL password.
## Helm chart structure

Our Helm chart will be structured as follows:

```plaintext
chart/
├── templates
│ ├── _helpers.tpl
│ ├── configmap.yaml
│ ├── cronjob.yaml
│ ├── deployment.yaml
│ ├── hpa.yaml # HorizontalPodAutoscaler
│ ├── ingress.yml
│ ├── pdb.yml # PodDisruptionBudget
│ ├── secret.yaml
│ └── service.yaml
├── Chart.yaml
├── secrets.yaml
└── values.yaml
```
Loading

0 comments on commit e53b146

Please sign in to comment.