1. Least Privileged User

By default, if no user is specified in the Dockerfile, it will use that root user, which can be a big security risk as 99% of applications don't need root, which can make it easier for an attacker to escalate the host's privileges .

To avoid this, just create a dedicated user and a dedicated group in the Dockerfile and add the USER directive to run the application with that user.

image.png

Note: some images already come with a generic user that we can use. For example, the node.js image comes with a file called node.js

3. Multi-stage build

Docker images are often much larger than they have to be, which ultimately affects our deployment, security, and development experience. Optimizing a build can be complicated because it's hard to keep the image clean and it ends up being messy and hard to follow. We also end up releasing unnecessary assets in our builds, such as tools, development dependencies, runtimes, or compilers

TLDR

The main idea is to separate the build phase from the runtime phase

  1. Derive from a base image with the entire runtime or SDK
  2. Copy our source code
  3. Install dependencies
  4. Generate build artifacts
  5. Copy the built artifacts to a smaller release image

Below is an example in Go, the final production image is only around 10mb

image.png

3. Scan for security vulnerabilities

Security is an important part of any application, especially if you work in a highly regulated industry like healthcare, finance, etc. Fortunately, docker comes with a docker scan command that scans our images for security holes. It is recommended to use it as part of the CI setup

docker scan <image>

node:17-alpine This is the scan result of node:17. As we can see node:17, the full OS distribution has a lot of vulnerabilities

image.png

image.png

4. Use a smaller size official image

Honestly, nobody likes pulling huge containers during development or CI builds. While sometimes we have to use a container with a full OS distribution for a specific task, it seems to me that a container should be small and just act as an isolated wrapper for our application when delivering code. Therefore, it is recommended to use smaller images of leaner OS distributions that only bundle the necessary system tools and libraries, while minimizing the attack surface and ensuring we have a more secure image

For example, alpine often uses a common practice when optimizing image size. Alpine Linux is a security-oriented lightweight Linux distribution based on musl libc and busybox

image.png

image.png

5. Use cache

As we all know, docker images contain various layers, and in a Dockerfile, each command or instruction creates an image layer. So if we rebuild our docker image and the Dockerfile or layers haven't changed, Docker will only use the cache layer to build the image. This significantly speeds up mirror rebuilds

Below is an example of how we can node_modules take advantage of the docker layer cache for caching. This can be achieved in multiple cases according to our Dockerfile

image.png

Likes(1)

Comment list count 0 Comments

No Comments