Container exec: Executing Commands in a running Container

We recently introduced the levv exec command as part of our ongoing effort to simplify running containers in production. This command is particularly valuable for inspecting and debugging running containers. In this tutorial, we’ll delve into common use cases for the exec command and highlight best practices to follow and anti-patterns to avoid.

Note: This tutorial examples use the levv exec command, but the same applies to docker exec.

Using the levv exec Command

Demonstration of the container exec command with levv exec

Executing a Command in a running Container

Executing a command in a running container is straightforward:

Example of executing a command in a container

Alternatively, you can also allocate a pseudo-tty using the --tty or -t option. This allows you to see the formatted output as you would if you were directly inside the container, rather than just receiving the output as plain text.

Example of executing a command in a container using the -t/--tty option

Executing a Command in the Background

If you want to execute a long-running command in your container (watch out for anti-patterns), you can do so by using the -d/--detach option.

$ levv exec -d my-svc curl <https://mirror.accum.se/mirror/wikimedia.org/dumps/>

This will execute the command in your container without blocking your active terminal window.

Running an Interactive Shell in a Container

Sometimes, you may need a more hands-on approach to interact with your container. You can start an interactive shell by using the -i/--interactive option.

$ levv exec -it my-service sh
/ # ls
bin   data  dev   etc   home  proc  root  sys   tmp   usr   var   www
/ # echo "Running in a shell inside my container"

This opens a shell in the specified container, allowing you to execute multiple commands interactively.

You’ll notice we also used the -t/--tty option. When using -i/--interactive, we always allocate a pseudo terminal. It will work without it but the interactivity will be less intuitive (go ahead and try it!).


Anti-Patterns

The exec command is powerful and practical as it makes it easy to interact with your containers. However, it needs to be used wisely or it might make you lose key benefits of containers like reproducibility.

Here we present the main exec anti-patterns.

Directly modifying Application State

One of the main advantage of containers is their reproducibility and predictability. When using exec to change an application configuration file or source code, we effectively lose those advantages. We expose ourselves to unplanned behavior and make it harder to debug in case of issue.

This is sometimes acceptable for development or staging applications to speed up the development cycle, but it should never be done in a production application.

Executing long-running Processes

Using exec to execute long processes is a major anti-pattern, as it can lead to resource contention and negatively impact your main application. For instance, instead of running a database dump using exec, you should use a shared volume and a separate container.

The same applies here: tolerated in dev/staging, never in production.

Relying on exec in your Production Workloads

You probably understood this from the previous anti-patterns: do not rely on exec for your production workloads.

Over-reliance on the exec command for making changes (like hot-fixes or worse changes in application state) in production environments can lead to unexpected behavior and challenges in maintaining consistency across your containers. You will lose de facto the scalability and predictability offered by containers.


Best Practices

Now that you know what to avoid, in this section we cover how to make the best use of exec.

But first, let’s examine security and the concept of immutability.

exec and Security

While the exec command is handy, it poses security risks. An attacker could exploit the command to execute malicious code within your container.

Another category of security risk is that you could inadvertently expose sensitive data in your output when executing commands in your container.

exec and Immutability

Immutability characterizes systems or components that cannot be modified after their creation. For containers, this means that instead of modifying it, we build a new image and re-deploy it entirely.

When working with containers in production, immutability is a good practice as it comes with operational benefits:

  • no configuration drift
  • change history
  • predictability

and security benefits:

  • easy auditing
  • reduced attack surface
  • integrity

Understanding the security and operational implications of using exec allows for more mature usage of the command.

On to the best practices:

Best Practice 1 : Use Specific Commands

Execute specific commands when interacting with your containers instead of opening interactive shells by default. It minimizes risk and ensures clarity in your needs when interacting with your container.

Best Practice 2 : Make Your Production Containers Immutable

Containers are not immutable by default. You need to actually make them so. Once your application is ready for production, you can update your Dockerfile to make your image immutable:

  • Don’t run as root: use the USER instruction
  • Remove shell access by deleting all binaries: RUN rm -rf /bin/*

You can also go one-step further by making your container filesystem read-only. This does not happen in the Dockerfile definition but at run time with the --read-only flag of docker run or with by setting read-only: true in your compose configuration.

Note: At the time of writing, levv does not yet support the read-only keyword.

If you make your filesystem read-only you will need to mount volumes at the path where your application needs to write under normal conditions.


With this article, we covered example use cases of the exec command, some anti-patterns to avoid when working with exec and security and operation best practices.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *