Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add minimal docker quickstart #1536

Merged
merged 17 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## What's New

* Auth Rate Limiter
* ziti edge quickstart command deprecates redundant --already-initialized flag. The identical behavior is implied by --home.

## Backwards compatibility

Expand Down
3 changes: 2 additions & 1 deletion quickstart/docker/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
ziti-bin
*/ziti-bin/**
*/persistent/**
2 changes: 2 additions & 0 deletions quickstart/docker/all-in-one/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# required until ziti 0.32.0
ZITI_QUICK_TAG=release-next
5 changes: 5 additions & 0 deletions quickstart/docker/all-in-one/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM debian:bookworm-slim

COPY ./build/ziti /usr/local/bin/

CMD ["ziti"]
122 changes: 122 additions & 0 deletions quickstart/docker/all-in-one/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# minimal Ziti Docker quickstart

This Docker Compose project runs `ziti edge quickstart` in a container while persisting configs, PKI, database, etc. in the same directory `./persistent/`.

## Run Ziti

This is the primary use case for this project: running the `ziti edge quickstart` command in the official
`openziti/ziti-cli` container image.

1. In this "minimal" sub-directory, pull the container images.

```bash
docker compose pull
```

2. Run the project.

```bash
docker compose up --detach
```

3. Modify the state in `./persistent/`, and bounce the container.

```bash
docker compose up --force-recreate --detach
```

4. Observe the logs

```bash
docker compose logs quickstart --follow
```

5. Run the CLI inside the quickstart environment.

```bash
docker compose exec quickstart ziti edge list identities
```

```buttonless title="Output"
╭────────────┬───────────────────┬─────────┬────────────┬─────────────╮
│ ID │ NAME │ TYPE │ ATTRIBUTES │ AUTH-POLICY │
├────────────┼───────────────────┼─────────┼────────────┼─────────────┤
│ ZS1YAo4Gnj │ quickstart-router │ Router │ │ Default │
│ cOmDAo4Gb │ Default Admin │ Default │ │ Default │
╰────────────┴───────────────────┴─────────┴────────────┴─────────────╯
results: 1-2 of 2
```

## Develop Ziti

This is a secondary use case for this Docker Compose project that replaces the `ziti` binary in the container image with
the one you build locally with `go build` before running the `ziti edge quickstart` command.

1. In the top-level directory of the `ziti` project, build the binary.

```bash
go build -o ./build ./...
```

The build command can also be run from this "minimal" sub-directory.

```bash
go build -o ../../../build ../../../...
```

2. In the "minimal" sub-directory, with `Dockerfile` present:

```bash
docker compose up --detach --build
```

By adding this `--build` option to the `up` command, the container image is built from the Dockerfile with your
locally built `ziti` binary instead of pulling the default `openziti/ziti-cli` container image from Docker Hub. In
the `compose.yml`, the Docker build context is defined with environment variable `ZITI_SRC_ROOT` which defaults to
`../../../` (three levels up from this directory at the top level of a Git working copy of the source repo).

### Troubleshooting

#### Changing File Locations

The Compose project file `compose.yml` and `Dockerfile` have file paths that represent the assumption they're placed in
a sub-directory three levels deep in a checked-out copy of the `openziti/ziti` source repository. This allows the Dockerfile
to copy the built binary from the top-level directory `./build`. You can move these files outside the source tree if you
adjust the paths in both files.

#### Building `ziti` in the Dockerfile

If the binary you build on your host doesn't run in the container due to an environment issue, such as a GLIBC version
mismatch, you have the option to build `ziti` in the container every time you run `up --build`.

Change `Dockerfile` like this, and run `docker compose up --detach --build` to build the checked-out source tree and run
the quickstart with the build.

```dockerfile
FROM golang:1.20-bookworm AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o ./build/ ./...

FROM debian:bookworm-slim
COPY --from=builder /app/build/ziti /usr/local/bin/

CMD ["ziti"]
```

#### Gotcha - Clobbering the Container Image

With `docker compose up --build`, the container image specified in `image` is replaced with the one built from the Dockerfile.
This clobbers any image you may have pulled from the registry unless you change the value of `image` or comment the line.

```yaml
# commenting "image" avoids clobbering the image pulled from the registry
# image: ${ZITI_QUICK_IMAGE:-docker.io/openziti/ziti-cli}:${ZITI_QUICK_TAG:-latest}
build:
context: ${ZITI_SRC_ROOT:-../../../}
dockerfile: ./quickstart/docker/minimal/Dockerfile
```

Next time you run `docker compose pull` the image from the registry will be refreshed in the local cache.
66 changes: 66 additions & 0 deletions quickstart/docker/all-in-one/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
services:
quickstart:
image: ${ZITI_QUICK_IMAGE:-docker.io/openziti/ziti-cli}:${ZITI_QUICK_TAG:-latest}
restart: unless-stopped
build:
context: ${ZITI_SRC_ROOT:-../../../}
dockerfile: ./quickstart/docker/all-in-one/Dockerfile
args: {}
networks:
quickstart:
# this allows other containers to use the same external DNS name to reach the quickstart container from within the
# Docker network that clients outside the Docker network use to reach the quickstart container via port forwarding
aliases:
- ${EXTERNAL_DNS:-null}
entrypoint:
- bash
- -euc
- |
ZITI_CMD+=" --ctrl-address ${EXTERNAL_DNS:-127.0.0.1}"\
" --ctrl-port ${ZITI_CTRL_EDGE_ADVERTISED_PORT:-1280}"\
" --router-address ${EXTERNAL_DNS:-127.0.0.1}"\
" --router-port ${ZITI_ROUTER_PORT:-3022}"\
" --password ${ZITI_PWD:-admin}"
echo "DEBUG: run command is: ziti $${@} $${ZITI_CMD}"
exec ziti "$${@}" $${ZITI_CMD}
command: -- edge quickstart --home /persistent
user: ${ZIGGY_UID:-1000}
environment:
HOME: /persistent
PFXLOG_NO_JSON: "${PFXLOG_NO_JSON:-true}"
volumes:
# store the quickstart state in a named volume; "initialize" service's mount must remain aligned to set the owner on
# "up"
- persistent:/persistent
# store the quickstart state on the Docker host in the same directory as this compose.yml file
# - ./persistent:/persistent
ports:
- ${ZITI_INTERFACE:-0.0.0.0}:${ZITI_CTRL_EDGE_ADVERTISED_PORT:-1280}:${ZITI_CTRL_EDGE_ADVERTISED_PORT:-1280}
- ${ZITI_INTERFACE:-0.0.0.0}:${ZITI_ROUTER_PORT:-3022}:${ZITI_ROUTER_PORT:-3022}
depends_on:
initialize:
condition: service_completed_successfully
# this service is used to initialize the persistent volume by setting the owner to the UID of the user running the
# quickstart container
initialize:
image: busybox
command: chown -Rc ${ZIGGY_UID:-1000} /persistent
user: root
environment:
HOME: /persistent
# PFXLOG_NO_JSON: "true"
volumes:
# store the quickstart state in a named volume; this mount must align with the "quickstart" service's mount
- persistent:/persistent
# store the quickstart state on the Docker host in the same directory as this compose.yml file
# - ./persistent:/persistent

# define a custom network so that we can also define a DNS alias for the quickstart container
networks:
quickstart:
driver: bridge

volumes:
# this will not be used if you switch from named volume to bind mount volume
persistent:
driver: local
139 changes: 70 additions & 69 deletions ziti/cmd/edge/quickstart.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,17 @@ func NewQuickStartCmd(out io.Writer, errOut io.Writer, context context.Context)
cmd := &cobra.Command{
Use: "quickstart",
Short: "runs a Controller and Router in quickstart mode",
Long: `runs a Controller and Router in quickstart mode. By default, this will create a totally ephemeral network, only valid while running.`,
Long: "runs a Controller and Router in quickstart mode with a temporary directory; suitable for testing and development",
Run: func(cmd *cobra.Command, args []string) {
options.out = out
options.errOut = errOut
options.run(context)
},
}
cmd.Flags().StringVarP(&options.Username, "username", "u", "", "Username to use when creating the Ziti Edge Controller. default: admin")
cmd.Flags().StringVarP(&options.Password, "password", "p", "", "Password to use for authenticating to the Ziti Edge Controller. default: admin")
cmd.Flags().StringVarP(&options.Username, "username", "u", "", "Admin username, default: admin")
cmd.Flags().StringVarP(&options.Password, "password", "p", "", "Admin password, default: admin")

cmd.Flags().BoolVar(&options.AlreadyInitialized, "already-initialized", false, "Specifies the PKI does not need to be created and the db does not need to be initialized. Recommended to be combined with --home. If --home is not specified the environment will be destroyed on shutdown! default: false")
cmd.Flags().StringVar(&options.Home, "home", "", "Sets the directory the environment should be installed into. Defaults to a temporary directory. If specified, the environment will not be removed on exit.")
cmd.Flags().StringVar(&options.Home, "home", "", "permanent directory")

cmd.Flags().StringVar(&options.ControllerAddress, "ctrl-address", "", "Sets the advertised address for the control plane and API. current: "+currentCtrlAddy)
cmd.Flags().Int16Var(&options.ControllerPort, "ctrl-port", int16(defautlCtrlPort), "Sets the port to use for the control plane and API. current: "+currentCtrlPort)
Expand All @@ -100,6 +99,8 @@ func (o *QuickstartOpts) run(ctx context.Context) {
tmpDir, _ := os.MkdirTemp("", "quickstart")
o.Home = tmpDir
o.cleanOnExit = true
} else {
logrus.Infof("permanent --home '%s' will not be removed on exit", o.Home)
}
if o.ControllerAddress != "" {
_ = os.Setenv(constants.CtrlAdvertisedAddressVarName, o.ControllerAddress)
Expand Down Expand Up @@ -141,12 +142,14 @@ func (o *QuickstartOpts) run(ctx context.Context) {
}

dbDir := o.Home + "/db"
_, _ = fmt.Fprintf(os.Stdout, "creating the tmp dir [%v] for the database.\n\n", dbDir)
_ = os.MkdirAll(dbDir, 0o777)
if _, err := os.Stat(dbDir); !os.IsNotExist(err) {
o.AlreadyInitialized = true
} else {
_ = os.MkdirAll(dbDir, 0o777)
logrus.Debugf("made directory '%s'", dbDir)

o.createMinimalPki()
o.createMinimalPki()

if !o.AlreadyInitialized {
ctrl := create.NewCmdCreateConfigController()
ctrl.SetArgs([]string{
fmt.Sprintf("--output=%s", ctrlYaml),
Expand Down Expand Up @@ -303,69 +306,67 @@ func (o *QuickstartOpts) run(ctx context.Context) {
}

func (o *QuickstartOpts) createMinimalPki() {
if !o.AlreadyInitialized {
where := o.Home + "/pki"
fmt.Println("emitting a minimal PKI")

//ziti pki create ca --pki-root="$pkiDir" --ca-file="root-ca" --ca-name="root-ca"
ca := pki.NewCmdPKICreateCA(o.out, o.errOut)
ca.SetArgs([]string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-file=%s", "root-ca"),
fmt.Sprintf("--ca-name=%s", "root-ca"),
})
pkiErr := ca.Execute()
if pkiErr != nil {
logrus.Fatal(pkiErr)
}
where := o.Home + "/pki"
fmt.Println("emitting a minimal PKI")

//ziti pki create ca --pki-root="$pkiDir" --ca-file="root-ca" --ca-name="root-ca"
ca := pki.NewCmdPKICreateCA(o.out, o.errOut)
ca.SetArgs([]string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-file=%s", "root-ca"),
fmt.Sprintf("--ca-name=%s", "root-ca"),
})
pkiErr := ca.Execute()
if pkiErr != nil {
logrus.Fatal(pkiErr)
}

//ziti pki create intermediate --pki-root "$pkiDir" --ca-name "root-ca" --intermediate-name "intermediate-ca" --intermediate-file "intermediate-ca" --max-path-len "1"
intermediate := pki.NewCmdPKICreateIntermediate(o.out, o.errOut)
intermediate.SetArgs([]string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-name=%s", "root-ca"),
fmt.Sprintf("--intermediate-name=%s", "intermediate-ca"),
fmt.Sprintf("--intermediate-file=%s", "intermediate-ca"),
"--max-path-len=1",
})
intErr := intermediate.Execute()
if intErr != nil {
logrus.Fatal(intErr)
}
//ziti pki create intermediate --pki-root "$pkiDir" --ca-name "root-ca" --intermediate-name "intermediate-ca" --intermediate-file "intermediate-ca" --max-path-len "1"
intermediate := pki.NewCmdPKICreateIntermediate(o.out, o.errOut)
intermediate.SetArgs([]string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-name=%s", "root-ca"),
fmt.Sprintf("--intermediate-name=%s", "intermediate-ca"),
fmt.Sprintf("--intermediate-file=%s", "intermediate-ca"),
"--max-path-len=1",
})
intErr := intermediate.Execute()
if intErr != nil {
logrus.Fatal(intErr)
}

//ziti pki create server --pki-root="${ZITI_HOME}/pki" --ca-name "intermediate-ca" --server-name "server" --server-file "server" --dns "localhost,${ZITI_HOSTNAME}"
svr := pki.NewCmdPKICreateServer(o.out, o.errOut)
var ips = "127.0.0.1,::1"
ip_override := os.Getenv("ZITI_CTRL_EDGE_IP_OVERRIDE")
if ip_override != "" {
ips = ips + "," + ip_override
}
svr.SetArgs([]string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-name=%s", "intermediate-ca"),
fmt.Sprintf("--server-name=%s", "server"),
fmt.Sprintf("--server-file=%s", "server"),
fmt.Sprintf("--dns=%s,%s", "localhost", helpers.GetCtrlAdvertisedAddress()),
fmt.Sprintf("--ip=%s", ips),
})
svrErr := svr.Execute()
if svrErr != nil {
logrus.Fatal(svrErr)
}
//ziti pki create server --pki-root="${ZITI_HOME}/pki" --ca-name "intermediate-ca" --server-name "server" --server-file "server" --dns "localhost,${ZITI_HOSTNAME}"
svr := pki.NewCmdPKICreateServer(o.out, o.errOut)
var ips = "127.0.0.1,::1"
ip_override := os.Getenv("ZITI_CTRL_EDGE_IP_OVERRIDE")
if ip_override != "" {
ips = ips + "," + ip_override
}
svr.SetArgs([]string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-name=%s", "intermediate-ca"),
fmt.Sprintf("--server-name=%s", "server"),
fmt.Sprintf("--server-file=%s", "server"),
fmt.Sprintf("--dns=%s,%s", "localhost", helpers.GetCtrlAdvertisedAddress()),
fmt.Sprintf("--ip=%s", ips),
})
svrErr := svr.Execute()
if svrErr != nil {
logrus.Fatal(svrErr)
}

//ziti pki create client --pki-root="${ZITI_HOME}/pki" --ca-name "intermediate-ca" --client-name "client" --client-file "client" --key-file "server"
client := pki.NewCmdPKICreateClient(o.out, o.errOut)
client.SetArgs([]string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-name=%s", "intermediate-ca"),
fmt.Sprintf("--client-name=%s", "client"),
fmt.Sprintf("--client-file=%s", "client"),
fmt.Sprintf("--key-file=%s", "server"),
})
clientErr := client.Execute()
if clientErr != nil {
logrus.Fatal(clientErr)
}
//ziti pki create client --pki-root="${ZITI_HOME}/pki" --ca-name "intermediate-ca" --client-name "client" --client-file "client" --key-file "server"
client := pki.NewCmdPKICreateClient(o.out, o.errOut)
client.SetArgs([]string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-name=%s", "intermediate-ca"),
fmt.Sprintf("--client-name=%s", "client"),
fmt.Sprintf("--client-file=%s", "client"),
fmt.Sprintf("--key-file=%s", "server"),
})
clientErr := client.Execute()
if clientErr != nil {
logrus.Fatal(clientErr)
}
}

Expand Down
Loading