#Containers running containers
1 messages ยท Page 1 of 1 (latest)
Can you give me a quick summary of your use case?
To make sure I give you a useful answer
as part of a CI/CD pipeline, I'd like to have a container that runs terraform apply. During this step, I'd like a parallel container to be started that copies a file in a google cloud bucket.
So there would be two containers: one runs terraform and starts this second container, and waits for it to complete before continuing
What language are you writing your Dagger Function in?
python, but I can read Golang
OK
So, the general idea is that your Dagger Function is just regular code (in your case Python) and you can use that to implement any logic you want. That includes running things concurrently. You simply use your native language's concurrency features. In Go that's goroutines and error groups. In Python, I think task groups?
Then anywhere in your code, you can make calls to the Dagger API. The Dagger engine itself can handle any number of calls concurrently. You just need to make sure your client calls are sufficiently concurrent
Then it's a matter of exploring the Dagger API docs, and finding the right calls for what you want to do
I've seen it implemented with python's async capabilities in the documentation. my main issue is how to have these two containers run as two distinct containers on the host, so that I can clearly separate the dependencies required to run them, so the one thing I'm missing is how I start a separate container from within a dagger function that's by default already running in the module's container
Lastly, you can also look in the Daggerverse for existing high-level functions, to save you time. For example, there are probably several terraform modules with ready-to-use functions for running "apply".
Can you give me an example of the code you would write for this? I can tell you if it works as intended.
In Dagger, everything is immutable, so if you make 2 calls to execute 2 containers, they will have nothing in common unless you explicitly pass the same input twice
Once you "grok" the basic concepts, Dagger code is very easy to reason about, because there are no side effects: all data flows in one direction through the dag
am writing you a quick example
@function
def run_terraform_code(self, terraform_dir: dagger.Directory) -> None:
#logic to run the terraform code with the given directory
@function
#function called within the terraform code; it starts a new container and performs the transfer
def transfer_to_bucket(self, creds: dagger.Secret, bucket_name, destination_filename) -> None:
#this is the part I wonder about, not the transferring logic but the making-a-new-parallel-container
@shell fog this is sort of how I was thinking of implementing it
what is the relationship between those 2 functions?
the first runs through a dagger call run_terraform_code, the second gets called within that terraform code
do you want to run terraform, then transfer the result?
gets called within that terraform code
Can you explain what you mean by this? You want run_terraform_code to call transfer_to_bucket ?
the dagger.Directory passed to run_terraform_code contains a series of .tf files necessary to spin-up a node on Google Cloud. there is one such .tf file that contains a call to the dagger engine currently running the dagger function; this call starts a new container that runs transfer_to_bucket
oh wow, so it's a dagger function calling terraform calling another dagger function
yeah, it's split this way so the dependencies necessary to run the terraform code are separated completely from the dependencies needed to run the transfer_to_bucket container
i think this is the way to do it but i may absolutely be wrong
Well, there are 2 separate topics here:
-
Whether you need to go through terraform to call another dagger function. If you're doing this specifically to workaround limitations of dagger, then you probably don't need to. But if you're doing it for unrelated constraints in your project (like, you just want to do it with terraform) that's fine, and we can make it work.
-
Assuming you do want to use terraform in this way, then how to get it to work
To address the first point: Dagger makes it very easy to run any number of containers each with their own dependencies, you have infinite control over this, and definitely don't need to wrap your function calls with terraform to achieve this.
As for point 2: regardless of point 1, if you do want to wrap a dagger call in terraform, it's perfectly possible. There is just one argument you need to pass to the withExec call running terraform, to enable "dagger in dagger" nesting.
(As a side note, I think it's time to enable that dagger-in-dagger nesting flag by default, wdyt @craggy lion @raw wolf ?)
wrt #1, it's to work around a limitation of terraform (there's a provisioning step that can't be taken care of in pure terraform and requires doing work outside of it, yet needs to account for it in order to completely represent the state of the infrastructure). I think we could potentially refactor this to work instead by making the call outside terraform, but then terraform wouldn't be able to work as a dashboard for the state of the infrastructure anymore
here's the thing: the call to dagger in the terraform code would ask the dagger engine running the function to start a separate container parallel to the first
so I want to avoid if possible nesting containers
You don't to worry about that, dagger-in-dagger nesting is not the same as nesting linux containers. From a system perspective they will not be nested.
ah fantastic!
It's more like unix processes forking more unix processes. Completely normal, and the system is designed for it.
It's one of the most powerful features and under-appreciated features of Dagger actually ๐
I see. so does in fact calling container.from(image).withExec(command) produce a parallel container, not a nested one?
The important thing is to set that flag I was talking about in withExec. Otherwise, the nested dagger call will not see an active session, and instead will try to bootstrap its own dagger engine - which will either fail, or actually result in a docker-in-docker situation (which you want to avoid)
ahhhh I see!
Yes, if by "nested" you mean "linux containers inside linux containers", then dagger will never nest containers
okay I see right
By the way, I recommend setting up a free Dagger Cloud account, and using the new Traces feature to visualize this
It just launched this week ๐
nice, will do!
i really think this stuff is about the revolutionize the game if it hasn't already
That's the plan ๐ Thanks for your patience
thanks for taking the time!
Yes, but would consider this PR to be a pre-req: https://github.com/dagger/dagger/pull/6916. Problem atm is dag-in-dag currently gets "too" isolated and is treated as a completely independent client, but that PR results in nested execs sharing the same server created by the original client while retaining isolation in the right parts (essentially, that PR makes nested execs the same as modules in terms of isolation). That PR keeps getting pushed in prio but hopefully will have time to finish it up soon.
Either way, I'll open an issue for enabling that by default once we can.
to be sure: although enabling experimental_privileged_nesting gives the original dagger engine access to the commands ran with with_exec, if I want conainer.from(image).with_exec to run dagger call dagger_function, I still need to install dagger in that container, correct?
either dagger or a custom dagger client, yes
would be cool if we added an API call to just mount that binary for you
There is a module for it though ๐
I see
oh nice! will look that up, thanks
Here's one I made (there are others) https://daggerverse.dev/mod/github.com/shykes/daggerverse/dagger
nice!!
Example of building the latest CLI release from source:
dagger call -m github.com/shykes/daggerverse/dagger@fcca1b35819c321058c0fb1392e58a17c5339c0f \
engine \
release --version=0.11.1 \
source \
cli --operating-system=linux --arch=x86_64 \
export --path=./dagger-0.11.1
very neat!
The equivalent in Go would be:
// Add the dagger CLI to a container
func addDaggerCLI(c *Container) *Container {
daggerCLI := dag.
Dagger().
Engine().
Release("0.11.1").
Source().
Cli()
return c.WithMountedFile("/bin/dagger", daggerCLI)
}
Assuming you've installed the module with dagger install github.com/shykes/daggerverse/dagger