Package your software
Antithesis runs your software using Linux container images, much as Docker and Kubernetes do. If you or your customers already deploy your software via containers, there’s a decent chance your existing images will work out of the box.
However there are a few common gotchas that we’ll go through here, the biggest of which is that Antithesis runs your system in a hermetic simulation environment. Everything your system depends upon must also be deployed into that environment, or suitably mocked.
For most customers, this is actually the most difficult part of using Antithesis, but the good news is that you have to do it approximately once. If you get stuck, please don’t hesitate to contact us.
1. Containerize your software
First, build a container image that includes your software and any of its dependencies. Again, if your software is already being deployed in containers, there’s a decent chance your existing images will just work.
Antithesis runs your software on x86-64 CPUs. Ensure that your software is compiled for this architecture by building containers with the following option: docker build --platform linux/amd64
.
If you operate a microservices architecture, you should provide a separate container for each of your services, though it’s fine if they share intermediate layers.
If your services perform network calls to the public web on startup, for example to fetch a software dependency or data file, you should move this into the container build process instead – remember, your software will be running without access to the internet.
For example, DON’T do this:
FROM docker.io/ubuntu:latest
COPY my_app /opt/my_app
CMD curl --fail https://example.com/data > /opt/data && /opt/my_app /opt/data
Do this instead:
FROM docker.io/ubuntu:latest
COPY my_app /opt/my_app
RUN curl --fail https://example.com/data > /opt/data
CMD /opt/my_app /opt/data
2. Containerize your dependencies
If your system depends on other first-party services that you control and operate, for example a local database node, it’s generally easiest to just put those in additional containers and run them in the Antithesis simulation as well. Later in this guide, we’ll show you how to use container orchestration to enable your containers to discover each other.
If your system depends on third-party services that you cannot run yourself (such as an API controlled by another company), then it’s easiest either to disable this part of your system’s functionality, or build a local mock that emulates their behavior.
This mock can run alongside your software in the same container, or it can run in a separate container and make itself available over the network. The latter enables Antithesis to perform fault injection on the connection between your software and the third-party service.
To help with this, we provide sophisticated local mocks or emulators for some of the most popular third-party dependencies. We also emulate many AWS APIs and services using Localstack. So if you’re already using Localstack in your integration tests, it should be straightforward to lift and shift to Antithesis.
3. Create a configuration directory
Next you’ll need to create a directory in your local filesystem, called config
, containing:
- Any configuration files expected by the containers: license files, settings, or other resources.
- A container orchestration file such as
docker-compose.yaml
.
Here are the contents of our example configuration directory:
$ ls -a config/
.
..
docker-compose.yaml
license
This directory will also get packaged as a container image and uploaded, but for now, you should just create it locally.
4. Set up container orchestration
Antithesis supports a few different orchestration technologies, but the one that’s easiest to set up is Docker Compose. This involves creating a file called docker-compose.yaml
that lists each of the services you want to have running, the container image that service should be started from, any external volumes that should be mounted into that image, and other options.
We recommend following this example closely, as there are a few important details to this that we discuss below.
version: '3.0'
services:
application1:
container_name: application1
hostname: application1
image: mycompany/app:antithesis-tag
networks:
antithesis-net:
ipv4_address: 10.20.20.1
application2:
container_name: application2
hostname: application2
image: mycompany/app:antithesis-tag
networks:
antithesis-net:
ipv4_address: 10.20.20.2
database:
container_name: database
hostname: database
image: docker.io/mysql:latest
volumes:
- ./volumes/database:/usr/bin/database/data
networks:
antithesis-net:
ipv4_address: 10.20.20.3
networks:
antithesis-net:
driver: bridge
ipam:
config:
- subnet: 10.20.20.0/24
A few items to note in this example:
- Multiple application services are being run from the same underlying container image, which allows you to test what happens when connections between your application servers fail.
- Some of the container images are coming from your internal container registry, and others (the MySQL dependency) are coming from a public Docker Hub repository. We support either option.
- Two of the services have requested external volume mounts. Data stored in these directories will be mapped out of the container filesystem, and will outlive the container’s own lifetime. The volume declarations refer to empty directories created in the configuration directory (see above). Since we’re going to use the configuration directory as our working directory, these relative paths will resolve correctly.
- The example explicitly defines a network and allocates static IPs to each of the services running on that network; both of these are necessary for Antithesis to work.
- In addition to network address, the services in the above example can reach each other by hostname. Our container orchestration will automatically generate suitable DNS entries from the names of each of the services, and inject them into the environment of every other service.
- Do not specify a
build
for any of your services in thisyaml
file: we’ll be running in a hermetic simulation environment, so all images it refers to need to be built before theyaml
file is called.
This is often the trickiest part of the process for users who don’t already deploy software in containers. If you simply follow the example above, you should be on solid ground, but you can also contact us if you need help here, or refer to the Docker Compose documentation.
5. Validating what you have so far
If you’ve followed the steps above, you should now be able to test your entire setup locally before deploying it to the Antithesis environment. Running docker-compose up
from the root of your configuration directory should produce a running system.
However, we want to test that this all still works without any access to the internet. The easiest way to do this is to first enter into a network namespace before running the command:
$ pwd
/home/user/config
$ unshare -n
[2] $ docker-compose up
...
[Lots of output]
...
If everything still comes up and works, your services all find each other, then the hardest part is done! Now you’ll need to add a test template to make it go.