commit
0940ffa495
5 changed files with 189 additions and 35 deletions
46
README.md
46
README.md
|
@ -1,31 +1,41 @@
|
||||||
# dockviz
|
# dockviz: Visualizing Docker Data
|
||||||
|
|
||||||
Visualizing Docker Data
|
This command takes Docker image and container information and presents in
|
||||||
|
different ways, to help you understand what's going on inside the system.
|
||||||
|
|
||||||
This command takes the raw Docker JSON and visualizes it in various ways.
|
# Quick Start
|
||||||
|
|
||||||
For image information, output can be formatted as
|
1. Download the [latest release](https://github.com/justone/dockviz/releases).
|
||||||
[Graphviz](http://www.graphviz.org), as a tree in the terminal, or in a short summary.
|
2. Visualize images by running `dockviz images -t`, which has similar output to `docker images -t`.
|
||||||
|
|
||||||
For container information, only Graphviz output has been implemented.
|
Image can be visualized as [Graphviz](http://www.graphviz.org), or as a tree or short summary in the terminal. Only Graphviz output has been implemented for containers.
|
||||||
|
|
||||||
# Examples
|
# Output Examples
|
||||||
|
|
||||||
## Containers
|
## Containers
|
||||||
|
|
||||||
Currently, containers are visualized with labeled lines for links. Containers that aren't running are greyed out.
|
Currently, containers are visualized with labeled lines for links. Containers that aren't running are greyed out.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dockviz containers -d | dot -Tpng -o containers.png
|
||||||
|
```
|
||||||
|
|
||||||
![](sample/containers.png "Container")
|
![](sample/containers.png "Container")
|
||||||
|
|
||||||
## Images
|
## Images
|
||||||
|
|
||||||
Image info is visualized with lines indicating parent images:
|
Image info is visualized with lines indicating parent images:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dockviz images -d | dot -Tpng -o images.png
|
||||||
|
```
|
||||||
|
|
||||||
![](sample/images.png "Image")
|
![](sample/images.png "Image")
|
||||||
|
|
||||||
Or in short form:
|
Or in short form:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
$ dockviz images -s
|
||||||
nate/mongodb: latest
|
nate/mongodb: latest
|
||||||
redis: latest
|
redis: latest
|
||||||
ubuntu: 12.04, precise, 12.10, quantal, 13.04, raring
|
ubuntu: 12.04, precise, 12.10, quantal, 13.04, raring
|
||||||
|
@ -34,6 +44,7 @@ ubuntu: 12.04, precise, 12.10, quantal, 13.04, raring
|
||||||
Or as a tree in the terminal:
|
Or as a tree in the terminal:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
$ dockviz images -t
|
||||||
└─511136ea3c5a Virtual Size: 0.0 B
|
└─511136ea3c5a Virtual Size: 0.0 B
|
||||||
|─f10ebce2c0e1 Virtual Size: 103.7 MB
|
|─f10ebce2c0e1 Virtual Size: 103.7 MB
|
||||||
| └─82cdea7ab5b5 Virtual Size: 103.9 MB
|
| └─82cdea7ab5b5 Virtual Size: 103.9 MB
|
||||||
|
@ -67,29 +78,21 @@ Or as a tree in the terminal:
|
||||||
|
|
||||||
# Running
|
# Running
|
||||||
|
|
||||||
## TCP
|
Dockviz supports connecting to the Docker daemon directly. It defaults to `unix:///var/run/docker.sock`, but respects the following as well:
|
||||||
|
|
||||||
When docker is listening on the TCP port:
|
* The `DOCKER_HOST`, `DOCKER_CERT_PATH`, and `DOCKER_TLS_VERIFY` environment variables, as set up by [boot2docker](http://boot2docker.io/) or [docker-machine](https://docs.docker.com/machine/).
|
||||||
|
* Command line arguments (e.g. `--tlscacert`), like those that Docker itself supports.
|
||||||
|
|
||||||
|
Dockviz also supports receiving Docker image or container json data on standard input.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -s http://localhost:4243/images/json?all=1 | dockviz images --tree
|
$ curl -s http://localhost:4243/images/json?all=1 | dockviz images --tree
|
||||||
$ curl -s http://localhost:4243/containers/json?all=1 | dockviz containers --dot | dot -Tpng -o containers.png
|
$ curl -s http://localhost:4243/containers/json?all=1 | dockviz containers --dot | dot -Tpng -o containers.png
|
||||||
```
|
|
||||||
|
|
||||||
## Socket
|
|
||||||
|
|
||||||
When docker is listening on the socket:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ echo -e "GET /images/json?all=1 HTTP/1.0\r\n" | nc -U /var/run/docker.sock | tail -n +5 | dockviz images --tree
|
$ echo -e "GET /images/json?all=1 HTTP/1.0\r\n" | nc -U /var/run/docker.sock | tail -n +5 | dockviz images --tree
|
||||||
$ echo -e "GET /containers/json?all=1 HTTP/1.0\r\n" | nc -U /var/run/docker.sock | tail -n +5 | dockviz containers --dot | dot -Tpng -o containers.png
|
$ echo -e "GET /containers/json?all=1 HTTP/1.0\r\n" | nc -U /var/run/docker.sock | tail -n +5 | dockviz containers --dot | dot -Tpng -o containers.png
|
||||||
```
|
```
|
||||||
|
|
||||||
GNU netcat doesn't support `-U` (UNIX socket) flag, so OpenBSD variant can be used.
|
Note: GNU netcat doesn't support `-U` (UNIX socket) flag, so OpenBSD variant can be used.
|
||||||
|
|
||||||
## Direct from Docker
|
|
||||||
|
|
||||||
Someday soon the Docker command line will allow dumping the image and container JSON directly.
|
|
||||||
|
|
||||||
# Binaries
|
# Binaries
|
||||||
|
|
||||||
|
@ -101,4 +104,3 @@ See the [releases](https://github.com/justone/dockviz/releases) area for binarie
|
||||||
go get ./...
|
go get ./...
|
||||||
go build
|
go build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
14
cli.go
14
cli.go
|
@ -1,19 +1,31 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/jessevdk/go-flags"
|
"github.com/jessevdk/go-flags"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GlobalOptions struct {
|
type GlobalOptions struct {
|
||||||
// no options yet
|
TLSCaCert string `long:"tlscacert" value-name:"~/.docker/ca.pem" description:"Trust certs signed only by this CA"`
|
||||||
|
TLSCert string `long:"tlscert" value-name:"~/.docker/cert.pem" description:"Path to TLS certificate file"`
|
||||||
|
TLSKey string `long:"tlskey" value-name:"~/.docker/key.pem" description:"Path to TLS key file"`
|
||||||
|
TLSVerify bool `long:"tlsverify" description:"Use TLS and verify the remote"`
|
||||||
|
Host string `long:"host" short:"H" value-name:"unix:///var/run/docker.sock" description:"Docker host to connect to"`
|
||||||
|
Version func() `long:"version" short:"v" description:"Display version information."`
|
||||||
}
|
}
|
||||||
|
|
||||||
var globalOptions GlobalOptions
|
var globalOptions GlobalOptions
|
||||||
var parser = flags.NewParser(&globalOptions, flags.Default)
|
var parser = flags.NewParser(&globalOptions, flags.Default)
|
||||||
|
|
||||||
|
var version = "v0.2"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
globalOptions.Version = func() {
|
||||||
|
fmt.Println("dockviz", version)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
if _, err := parser.Parse(); err != nil {
|
if _, err := parser.Parse(); err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/fsouza/go-dockerclient"
|
||||||
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -28,16 +30,51 @@ var containersCommand ContainersCommand
|
||||||
|
|
||||||
func (x *ContainersCommand) Execute(args []string) error {
|
func (x *ContainersCommand) Execute(args []string) error {
|
||||||
|
|
||||||
|
var containers *[]Container
|
||||||
|
|
||||||
|
stat, err := os.Stdin.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading stdin stat", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat.Mode() & os.ModeCharDevice) == 0 {
|
||||||
// read in stdin
|
// read in stdin
|
||||||
stdin, err := ioutil.ReadAll(os.Stdin)
|
stdin, err := ioutil.ReadAll(os.Stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error reading all input", err)
|
return fmt.Errorf("error reading all input", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
containers, err := parseContainersJSON(stdin)
|
containers, err = parseContainersJSON(stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
client, err := connect()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientContainers, err := client.ListContainers(docker.ListContainersOptions{All: true})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var conts []Container
|
||||||
|
for _, container := range clientContainers {
|
||||||
|
conts = append(conts, Container{
|
||||||
|
container.ID,
|
||||||
|
container.Image,
|
||||||
|
container.Names,
|
||||||
|
apiPortToMap(container.Ports),
|
||||||
|
container.Created,
|
||||||
|
container.Status,
|
||||||
|
container.Command,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
containers = &conts
|
||||||
|
}
|
||||||
|
|
||||||
if containersCommand.Dot {
|
if containersCommand.Dot {
|
||||||
fmt.Printf(jsonContainersToDot(containers))
|
fmt.Printf(jsonContainersToDot(containers))
|
||||||
|
@ -48,6 +85,20 @@ func (x *ContainersCommand) Execute(args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiPortToMap(ports []docker.APIPort) []map[string]interface{} {
|
||||||
|
result := make([]map[string]interface{}, 2)
|
||||||
|
for _, port := range ports {
|
||||||
|
intPort := map[string]interface{}{
|
||||||
|
"IP": port.IP,
|
||||||
|
"Type": port.Type,
|
||||||
|
"PrivatePort": port.PrivatePort,
|
||||||
|
"PublicPort": port.PublicPort,
|
||||||
|
}
|
||||||
|
result = append(result, intPort)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func parseContainersJSON(rawJSON []byte) (*[]Container, error) {
|
func parseContainersJSON(rawJSON []byte) (*[]Container, error) {
|
||||||
|
|
||||||
var containers []Container
|
var containers []Container
|
||||||
|
|
40
images.go
40
images.go
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/fsouza/go-dockerclient"
|
||||||
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -29,17 +31,53 @@ var imagesCommand ImagesCommand
|
||||||
|
|
||||||
func (x *ImagesCommand) Execute(args []string) error {
|
func (x *ImagesCommand) Execute(args []string) error {
|
||||||
|
|
||||||
|
var images *[]Image
|
||||||
|
|
||||||
|
stat, err := os.Stdin.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading stdin stat", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat.Mode() & os.ModeCharDevice) == 0 {
|
||||||
// read in stdin
|
// read in stdin
|
||||||
stdin, err := ioutil.ReadAll(os.Stdin)
|
stdin, err := ioutil.ReadAll(os.Stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error reading all input", err)
|
return fmt.Errorf("error reading all input", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
images, err := parseImagesJSON(stdin)
|
images, err = parseImagesJSON(stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
client, err := connect()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientImages, err := client.ListImages(docker.ListImagesOptions{All: true})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ims []Image
|
||||||
|
for _, image := range clientImages {
|
||||||
|
// fmt.Println(image)
|
||||||
|
ims = append(ims, Image{
|
||||||
|
image.ID,
|
||||||
|
image.ParentID,
|
||||||
|
image.RepoTags,
|
||||||
|
image.VirtualSize,
|
||||||
|
image.Size,
|
||||||
|
image.Created,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
images = &ims
|
||||||
|
}
|
||||||
|
|
||||||
if imagesCommand.Dot {
|
if imagesCommand.Dot {
|
||||||
fmt.Printf(jsonToDot(images))
|
fmt.Printf(jsonToDot(images))
|
||||||
} else if imagesCommand.Tree {
|
} else if imagesCommand.Tree {
|
||||||
|
|
51
util.go
Normal file
51
util.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/fsouza/go-dockerclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
func connect() (*docker.Client, error) {
|
||||||
|
|
||||||
|
// grab directly from docker daemon
|
||||||
|
var endpoint string
|
||||||
|
if env_endpoint := os.Getenv("DOCKER_HOST"); len(env_endpoint) > 0 {
|
||||||
|
endpoint = env_endpoint
|
||||||
|
} else if len(globalOptions.Host) > 0 {
|
||||||
|
endpoint = globalOptions.Host
|
||||||
|
} else {
|
||||||
|
// assume local socket
|
||||||
|
endpoint = "unix:///var/run/docker.sock"
|
||||||
|
}
|
||||||
|
|
||||||
|
var client *docker.Client
|
||||||
|
var err error
|
||||||
|
dockerTlsVerifyEnv := os.Getenv("DOCKER_TLS_VERIFY")
|
||||||
|
if dockerTlsVerifyEnv == "1" || globalOptions.TLSVerify {
|
||||||
|
if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); len(dockerCertPath) > 0 {
|
||||||
|
cert := path.Join(dockerCertPath, "cert.pem")
|
||||||
|
key := path.Join(dockerCertPath, "key.pem")
|
||||||
|
ca := path.Join(dockerCertPath, "ca.pem")
|
||||||
|
client, err = docker.NewTLSClient(endpoint, cert, key, ca)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else if len(globalOptions.TLSCert) > 0 && len(globalOptions.TLSKey) > 0 && len(globalOptions.TLSCaCert) > 0 {
|
||||||
|
client, err = docker.NewTLSClient(endpoint, globalOptions.TLSCert, globalOptions.TLSKey, globalOptions.TLSCaCert)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("TLS Verification requested but certs not specified")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client, err = docker.NewClient(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
Loading…
Reference in a new issue