This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
dagger/docs/learn/1003-get-started.md
Richard Jones ef66f0bc69 new getting started tutorial
Signed-off-by: Richard Jones <richard@dagger.io>
2021-10-05 13:33:05 -06:00

7.9 KiB
Raw Blame History

slug
/1003/get-started/

Get Started with Dagger

In this tutorial, you will learn the basics of Dagger by building a Dagger project from scratch. This simple project deploys a React application to your local machine via docker. In later tutorials, you will learn how to configure Dagger to deploy to your infrastructure. And, for advanced users, how to share access to your infrastructure in the same way that we share access to ours now.

This tutorial does involve writing CUE, so if you havent already, be sure to read What is CUE?

In this tutorial we will learn:

  • How to initialize and structure a Dagger project
  • About Dagger concepts such as
    • the plan
    • environments
    • inputs and outputs
  • How to write CUE for Dagger
  • How to deploy an application using dagger up

Deploy an Application Locally

The following instructions assume you are working locally, but could just as easily be run on a remote machine into which you have a shell.

Install Dagger

First, make sure you have installed Dagger. You can run dagger version to ensure you have the latest installed and working.

Create a Dagger Project

First clone the Dagger examples repository, change directories to the todoapp/ and list its contents:

Note that all tutorials will operate from the todoapp directory.

git clone https://github.com/dagger/examples.git
cd examples/todoapp
ls -la

This React application will use Yarn to build a static website with the following directories and files.

-rw-r--r--   ...     794 Sep  7 10:09 package.json
drwxr-xr-x   ...     256 Sep  7 10:09 public
drwxr-xr-x   ...     192 Sep 29 11:17 src
-rw-r--r--   ...  465514 Sep 29 11:17 yarn.lock

Now we need to initialize this directory as a Dagger project (and relist directories):

dagger init
ls -la

You will now see 2 new directories:

  • The .dagger directory will store metadata about environments, inputs, and outputs which we will cover later.
  • The cue.mod directory stores libraries such as dagger/universe which can be imported into your Dagger plan.

Dagger will load all .cue files recursively in the current Dagger project. More directories can be added to help organize code.

Note that Dagger, like the CUE CLI command, will only load CUE files from the cue.mod directory in response to import statements.

Write a Dagger Plan

A Dagger plan is written in CUE and defines the resources, dependencies, and logic to deploy an application to an environment. Unlike traditional glue code written in a scripting language such as Bash or PowerShell, a Dagger plan is declarative rather than imperative. This frees us from thinking about the order of operations, since Dagger will infer dependendencies and calculate correct order on its own.

Let's first create a directory to hold our plan separately from our application code:

mkdir -p ./plans/local

We will now create the following files:

  • plans/todoapp.cue which will define resources common to all environments
  • plans/local/local.cue which will define resources specific to the local environment

Create the file plans/todoapp.cue with the following content:

package todoapp

import (
  "alpha.dagger.io/dagger"
  "alpha.dagger.io/os"
  "alpha.dagger.io/docker"
  "alpha.dagger.io/js/yarn"
)

// Build the source code using Yarn
app: yarn.#Package & {
  "source": dagger.#Artifact & dagger.#Input
}

// package the staic HTML from yarn into a Docker image
image: os.#Container & {
  image: docker.#Pull & {
    from: "nginx"
  }

  copy: "/usr/share/nginx/html": from: app.build
}

// push the image to a registry
push: docker.#Push & {
  // leave target as a string here so that 
  // different environments can push to different registries
  target: string 
  source: image
}

This file will define the resources and relationships between them that are common across all environments. For example here we are deploying to our local Docker engine in our local environment, but for staging or production as examples, we would deploy to some other container orchestration system such as Kubernetes somewhere out there among the various cloud providers.

Create the file plans/local/local.cue with the following content:

package todoapp

import (
  "alpha.dagger.io/dagger"
  "alpha.dagger.io/docker"
)

// run our todoapp in our local Docker engine
todoapp: docker.#Run & {
  ref: push.ref
  name: "todoapp"
  ports: ["8080:80"]
  socket: dagger.#Stream & dagger.#Input
}

// push to our local registry
push: target: "localhost:5000/todoapp"

Notice that both files have the same package todoapp declared on the first line. This is crucial to inform CUE that they are to be loaded and evaluated together in the same context.

Our local.cue file now holds resources specific to our local environment. Also notice that we are defining a concrete value for the target key here. The entire push object is defined in both files and CUE will merge the values found among our 2 files.

Create an Environment

Before we can deploy the plan, we need to define an environment which is the specific plan to execute, as well as the context from which inputs are pulled and to which state is stored.

In this example we will deploy the app to our local docker engine so lets create a local environment:

dagger new local -p ./plans/local
dagger list

The list command shows the current environments defined:

local ...todoapp/.dagger/env/local

Define Input Values per Environment

Our Dagger plan includes a number of references to dagger.#Input which inform the Dagger engine that the concrete value should be pulled from inputs. While some things such as the registry target we saw above can be expressed purely in CUE, others such as directories, secrets, and sockets are required to be explicitly defined as inputs to ensure security. If Dagger allowed such things to be stated in CUE, the entire package system could become a source of attacks.

List the inputs Dagger is aware of according to our plan:

dagger -e local input list

You should see the following output:

Input           Value             Set by user  Description
app.source      dagger.#Artifact  false         Application source code
todoapp.socket  struct            false         Mount local docker socket

Notice that Set by user is false for both, because we have not yet provided Dagger with those values.

Lets provide them now:

dagger -e local input socket todoapp.socket /var/run/docker.sock
dagger -e local input dir app.source ./

Now let's replay the dagger input list command:

Input           Value             Set by user  Description
app.source      dagger.#Artifact  true         Application source code
todoapp.socket  struct            true         Mount local docker socket

Notice that Dagger now reports that both inputs have been set.

Deploy the Appplication

With our plan in place, our environment set, and our inputs defined we can deploy the application:

dagger up

Once complete you should get logs, and a final output like this:

Output                     Value                                          Description
app.build                  struct                                         Build output directory
push.ref                   "localhost:5000/todoapp:latest@sha256:<hash>"  Image ref
push.digest                "sha256:<hash>"                                Image digest
todoapp.ref                "localhost:5000/todoapp:latest@sha256:<hash>"  Image reference (e.g: nginx:alpine)
todoapp.run.env.IMAGE_REF  "localhost:5000/todoapp:latest@sha256:<hash>"  -

Congratulations! Youve deployed your first Dagger plan! You can now open http://localhost:8080 in your browser!