diff --git a/README.md b/README.md index 3275f9b5..ad24d69a 100644 --- a/README.md +++ b/README.md @@ -57,3 +57,23 @@ To run it use: ### Push the service to Docker Container Registry `GROUP=weaveworksdemos COMMIT=test ./scripts/push.sh` + +## Test Zipkin + +To test with Zipkin + +``` +docker-compose -f docker-compose-zipkin.yml build +docker-compose -f docker-compose-zipkin.yml up +``` +It takes about 10 seconds to seed data + +you should see it at: +[http://localhost:9411/](http://localhost:9411) + +be sure to hit the "Find Traces" button. You may need to reload the page. + +when done you can run: +``` +docker-compose -f docker-compose-zipkin.yml down +``` diff --git a/cmd/cataloguesvc/main.go b/cmd/cataloguesvc/main.go index acd51ac0..545f0352 100644 --- a/cmd/cataloguesvc/main.go +++ b/cmd/cataloguesvc/main.go @@ -9,15 +9,22 @@ import ( "github.com/go-kit/kit/log" kitprometheus "github.com/go-kit/kit/metrics/prometheus" + stdopentracing "github.com/opentracing/opentracing-go" + zipkin "github.com/openzipkin/zipkin-go-opentracing" stdprometheus "github.com/prometheus/client_golang/prometheus" "net/http" + "path/filepath" + _ "github.com/go-sql-driver/mysql" "github.com/jmoiron/sqlx" "github.com/microservices-demo/catalogue" "golang.org/x/net/context" - "path/filepath" +) + +const ( + ServiceName = "catalogue" ) func main() { @@ -25,6 +32,7 @@ func main() { port = flag.String("port", "8081", "Port to bind HTTP listener") // TODO(pb): should be -addr, default ":8081" images = flag.String("images", "./images/", "Image path") dsn = flag.String("DSN", "catalogue_user:default_password@tcp(catalogue-db:3306)/socksdb", "Data Source Name: [username[:password]@][protocol[(address)]]/dbname") + zip = flag.String("zipkin", os.Getenv("ZIPKIN"), "Zipkin address") ) flag.Parse() @@ -48,6 +56,32 @@ func main() { logger = log.NewContext(logger).With("caller", log.DefaultCaller) } + var tracer stdopentracing.Tracer + { + if *zip == "" { + tracer = stdopentracing.NoopTracer{} + } else { + logger := log.NewContext(logger).With("tracer", "Zipkin") + logger.Log("addr", zip) + collector, err := zipkin.NewHTTPCollector( + *zip, + zipkin.HTTPLogger(logger), + ) + if err != nil { + logger.Log("err", err) + os.Exit(1) + } + tracer, err = zipkin.NewTracer( + zipkin.NewRecorder(collector, false, fmt.Sprintf("localhost:%v", port), ServiceName), + ) + if err != nil { + logger.Log("err", err) + os.Exit(1) + } + } + stdopentracing.InitGlobalTracer(tracer) + } + // Data domain. db, err := sqlx.Open("mysql", *dsn) if err != nil { @@ -88,12 +122,12 @@ func main() { } // Endpoint domain. - endpoints := catalogue.MakeEndpoints(service) + endpoints := catalogue.MakeEndpoints(service, tracer) // Create and launch the HTTP server. go func() { logger.Log("transport", "HTTP", "port", *port) - handler := catalogue.MakeHTTPHandler(ctx, endpoints, *images, logger) + handler := catalogue.MakeHTTPHandler(ctx, endpoints, *images, logger, tracer) errc <- http.ListenAndServe(":"+*port, handler) }() diff --git a/docker-compose-zipkin.yml b/docker-compose-zipkin.yml new file mode 100644 index 00000000..bbbd0d81 --- /dev/null +++ b/docker-compose-zipkin.yml @@ -0,0 +1,52 @@ +version: '2' + +services: + catalogue: + build: + context: . + dockerfile: ./docker/catalogue/Dockerfile + image: weaveworksdemos/catalogue-dev + hostname: catalogue + restart: always + cap_drop: + - all + cap_add: + - NET_BIND_SERVICE + read_only: true + environment: + - ZIPKIN=http://zipkin:9411/api/v1/spans + - reschedule=on-node-failure + ports: + - "8080:80" + catalogue-db: + build: + context: ./docker/catalogue-db/ + image: weaveworksdemos/catalogue-dev-db + hostname: catalogue-db + restart: always + environment: + - reschedule=on-node-failure + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_ALLOW_EMPTY_PASSWORD=true + - MYSQL_DATABASE=socksdb + zipkin: + image: openzipkin/zipkin + hostname: zipkin + restart: always + cap_drop: + - all + cap_add: + - CHOWN + - SETGID + - SETUID + read_only: true + tmpfs: + - /tmp:rw,noexec,nosuid + environment: + - reschedule=on-node-failure + ports: + - "9411:9411" + zipkinseed: + image: alpine + command: /bin/sh -c 'sleep 10 ; wget http://catalogue/health ; wget http://catalogue/catalogue ; wget http://catalogue/catalogue/size ; wget http://catalogue/tags' + diff --git a/endpoints.go b/endpoints.go index b3860330..1aa2b82b 100644 --- a/endpoints.go +++ b/endpoints.go @@ -6,6 +6,8 @@ package catalogue import ( "github.com/go-kit/kit/endpoint" + "github.com/go-kit/kit/tracing/opentracing" + stdopentracing "github.com/opentracing/opentracing-go" "golang.org/x/net/context" ) @@ -20,13 +22,13 @@ type Endpoints struct { // MakeEndpoints returns an Endpoints structure, where each endpoint is // backed by the given service. -func MakeEndpoints(s Service) Endpoints { +func MakeEndpoints(s Service, tracer stdopentracing.Tracer) Endpoints { return Endpoints{ - ListEndpoint: MakeListEndpoint(s), - CountEndpoint: MakeCountEndpoint(s), - GetEndpoint: MakeGetEndpoint(s), - TagsEndpoint: MakeTagsEndpoint(s), - HealthEndpoint: MakeHealthEndpoint(s), + ListEndpoint: opentracing.TraceServer(tracer, "GET /catalogue")(MakeListEndpoint(s)), + CountEndpoint: opentracing.TraceServer(tracer, "GET /catalogue/size")(MakeCountEndpoint(s)), + GetEndpoint: opentracing.TraceServer(tracer, "GET /catalogue/{id}")(MakeGetEndpoint(s)), + TagsEndpoint: opentracing.TraceServer(tracer, "GET /tags")(MakeTagsEndpoint(s)), + HealthEndpoint: opentracing.TraceServer(tracer, "GET /health")(MakeHealthEndpoint(s)), } } diff --git a/transport.go b/transport.go index f72ce81d..426e3f2e 100644 --- a/transport.go +++ b/transport.go @@ -10,14 +10,16 @@ import ( "strings" "github.com/go-kit/kit/log" + "github.com/go-kit/kit/tracing/opentracing" httptransport "github.com/go-kit/kit/transport/http" "github.com/gorilla/mux" + stdopentracing "github.com/opentracing/opentracing-go" "github.com/prometheus/client_golang/prometheus/promhttp" "golang.org/x/net/context" ) // MakeHTTPHandler mounts the endpoints into a REST-y HTTP handler. -func MakeHTTPHandler(ctx context.Context, e Endpoints, imagePath string, logger log.Logger) http.Handler { +func MakeHTTPHandler(ctx context.Context, e Endpoints, imagePath string, logger log.Logger, tracer stdopentracing.Tracer) http.Handler { r := mux.NewRouter().StrictSlash(false) options := []httptransport.ServerOption{ httptransport.ServerErrorLogger(logger), @@ -35,28 +37,28 @@ func MakeHTTPHandler(ctx context.Context, e Endpoints, imagePath string, logger e.ListEndpoint, decodeListRequest, encodeListResponse, - options..., + append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "GET /catalogue", logger)))..., )) r.Methods("GET").Path("/catalogue/size").Handler(httptransport.NewServer( ctx, e.CountEndpoint, decodeCountRequest, encodeResponse, - options..., + append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "GET /catalogue/size", logger)))..., )) r.Methods("GET").Path("/catalogue/{id}").Handler(httptransport.NewServer( ctx, e.GetEndpoint, decodeGetRequest, encodeGetResponse, // special case, this one can have an error - options..., + append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "GET /catalogue/{id}", logger)))..., )) r.Methods("GET").Path("/tags").Handler(httptransport.NewServer( ctx, e.TagsEndpoint, decodeTagsRequest, encodeResponse, - options..., + append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "GET /tags", logger)))..., )) r.Methods("GET").PathPrefix("/catalogue/images/").Handler(http.StripPrefix( "/catalogue/images/", @@ -67,7 +69,7 @@ func MakeHTTPHandler(ctx context.Context, e Endpoints, imagePath string, logger e.HealthEndpoint, decodeHealthRequest, encodeHealthResponse, - options..., + append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "GET /health", logger)))..., )) r.Handle("/metrics", promhttp.Handler()) return r diff --git a/vendor/manifest b/vendor/manifest index 329e5cee..dd808ac3 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -1,6 +1,14 @@ { "version": 0, "dependencies": [ + { + "importpath": "github.com/Shopify/sarama", + "repository": "https://github.com/Shopify/sarama", + "vcs": "git", + "revision": "0fb560e5f7fbcaee2f75e3c34174320709f69944", + "branch": "master", + "notests": true + }, { "importpath": "github.com/VividCortex/gohistogram", "repository": "https://github.com/VividCortex/gohistogram", @@ -9,6 +17,15 @@ "branch": "master", "notests": true }, + { + "importpath": "github.com/apache/thrift/lib/go/thrift", + "repository": "https://github.com/apache/thrift", + "vcs": "git", + "revision": "30a8b65dcec88d0710143ca2b94f71915a8549c5", + "branch": "master", + "path": "/lib/go/thrift", + "notests": true + }, { "importpath": "github.com/beorn7/perks/quantile", "repository": "https://github.com/beorn7/perks", @@ -34,6 +51,40 @@ "branch": "master", "notests": true }, + { + "importpath": "github.com/davecgh/go-spew/spew", + "repository": "https://github.com/davecgh/go-spew", + "vcs": "git", + "revision": "346938d642f2ec3594ed81d874461961cd0faa76", + "branch": "master", + "path": "/spew", + "notests": true + }, + { + "importpath": "github.com/eapache/go-resiliency/breaker", + "repository": "https://github.com/eapache/go-resiliency", + "vcs": "git", + "revision": "b86b1ec0dd4209a588dc1285cdd471e73525c0b3", + "branch": "master", + "path": "/breaker", + "notests": true + }, + { + "importpath": "github.com/eapache/go-xerial-snappy", + "repository": "https://github.com/eapache/go-xerial-snappy", + "vcs": "git", + "revision": "bb955e01b9346ac19dc29eb16586c90ded99a98c", + "branch": "master", + "notests": true + }, + { + "importpath": "github.com/eapache/queue", + "repository": "https://github.com/eapache/queue", + "vcs": "git", + "revision": "44cc805cf13205b55f69e14bcb69867d1ae92f98", + "branch": "master", + "notests": true + }, { "importpath": "github.com/go-kit/kit/endpoint", "repository": "https://github.com/go-kit/kit", @@ -61,6 +112,15 @@ "path": "metrics", "notests": true }, + { + "importpath": "github.com/go-kit/kit/tracing/opentracing", + "repository": "https://github.com/go-kit/kit", + "vcs": "git", + "revision": "905b60253dbac4a14625ce52217ede00d3a3aca7", + "branch": "master", + "path": "/tracing/opentracing", + "notests": true + }, { "importpath": "github.com/go-kit/kit/transport/http", "repository": "https://github.com/go-kit/kit", @@ -103,6 +163,33 @@ "branch": "master", "notests": true }, + { + "importpath": "github.com/gogo/protobuf/proto", + "repository": "https://github.com/gogo/protobuf", + "vcs": "git", + "revision": "84af2615df1ba1d35cc975ba94b64ee67d6c196e", + "branch": "master", + "path": "/proto", + "notests": true + }, + { + "importpath": "github.com/gogo/protobuf/sortkeys", + "repository": "https://github.com/gogo/protobuf", + "vcs": "git", + "revision": "84af2615df1ba1d35cc975ba94b64ee67d6c196e", + "branch": "master", + "path": "sortkeys", + "notests": true + }, + { + "importpath": "github.com/gogo/protobuf/types", + "repository": "https://github.com/gogo/protobuf", + "vcs": "git", + "revision": "84af2615df1ba1d35cc975ba94b64ee67d6c196e", + "branch": "master", + "path": "types", + "notests": true + }, { "importpath": "github.com/golang/protobuf/proto", "repository": "https://github.com/golang/protobuf", @@ -121,6 +208,14 @@ "path": "ptypes/any", "notests": true }, + { + "importpath": "github.com/golang/snappy", + "repository": "https://github.com/golang/snappy", + "vcs": "git", + "revision": "d9eb7a3d35ec988b8585d4a0068e462c27d28380", + "branch": "master", + "notests": true + }, { "importpath": "github.com/gorilla/context", "repository": "https://github.com/gorilla/context", @@ -196,6 +291,14 @@ "branch": "master", "notests": true }, + { + "importpath": "github.com/klauspost/crc32", + "repository": "https://github.com/klauspost/crc32", + "vcs": "git", + "revision": "cb6bfca970f6908083f26f39a79009d608efd5cd", + "branch": "master", + "notests": true + }, { "importpath": "github.com/kr/logfmt", "repository": "https://github.com/kr/logfmt", @@ -213,6 +316,23 @@ "path": "/pbutil", "notests": true }, + { + "importpath": "github.com/opentracing/opentracing-go", + "repository": "https://github.com/opentracing/opentracing-go", + "vcs": "git", + "revision": "ac5446f53f2c0fc68dc16dc5f426eae1cd288b34", + "branch": "master", + "notests": true + }, + { + "importpath": "github.com/openzipkin/zipkin-go-opentracing", + "repository": "https://github.com/openzipkin/zipkin-go-opentracing", + "vcs": "git", + "revision": "594640b9ef7e5c994e8d9499359d693c032d738c", + "branch": "master", + "notests": true, + "allfiles": true + }, { "importpath": "github.com/pborman/uuid", "repository": "https://github.com/pborman/uuid", @@ -229,6 +349,23 @@ "branch": "master", "notests": true }, + { + "importpath": "github.com/pierrec/lz4", + "repository": "https://github.com/pierrec/lz4", + "vcs": "git", + "revision": "5c9560bfa9ace2bf86080bf40d46b34ae44604df", + "branch": "master", + "notests": true + }, + { + "importpath": "github.com/pierrec/xxHash/xxHash32", + "repository": "https://github.com/pierrec/xxHash", + "vcs": "git", + "revision": "5a004441f897722c627870a981d02b29924215fa", + "branch": "master", + "path": "/xxHash32", + "notests": true + }, { "importpath": "github.com/prometheus/client_golang/prometheus", "repository": "https://github.com/prometheus/client_golang", @@ -282,6 +419,52 @@ "branch": "master", "notests": true }, + { + "importpath": "github.com/rcrowley/go-metrics", + "repository": "https://github.com/rcrowley/go-metrics", + "vcs": "git", + "revision": "1f30fe9094a513ce4c700b9a54458bbb0c96996c", + "branch": "master", + "notests": true + }, + { + "importpath": "github.com/stathat/go", + "repository": "https://github.com/stathat/go", + "vcs": "git", + "revision": "74669b9f388d9d788c97399a0824adbfee78400e", + "branch": "master", + "notests": true + }, + { + "importpath": "github.com/stretchr/testify/assert", + "repository": "https://github.com/stretchr/testify", + "vcs": "git", + "revision": "2402e8e7a02fc811447d11f881aa9746cdc57983", + "branch": "master", + "path": "/assert", + "notests": true, + "allfiles": true + }, + { + "importpath": "github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew", + "repository": "https://github.com/stretchr/testify", + "vcs": "git", + "revision": "2402e8e7a02fc811447d11f881aa9746cdc57983", + "branch": "master", + "path": "vendor/github.com/davecgh/go-spew/spew", + "notests": true, + "allfiles": true + }, + { + "importpath": "github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib", + "repository": "https://github.com/stretchr/testify", + "vcs": "git", + "revision": "2402e8e7a02fc811447d11f881aa9746cdc57983", + "branch": "master", + "path": "vendor/github.com/pmezard/go-difflib/difflib", + "notests": true, + "allfiles": true + }, { "importpath": "golang.org/x/net/context", "repository": "https://go.googlesource.com/net", @@ -291,6 +474,24 @@ "path": "/context", "notests": true }, + { + "importpath": "golang.org/x/net/internal/timeseries", + "repository": "https://go.googlesource.com/net", + "vcs": "git", + "revision": "45e771701b814666a7eb299e6c7a57d0b1799e91", + "branch": "master", + "path": "internal/timeseries", + "notests": true + }, + { + "importpath": "golang.org/x/net/trace", + "repository": "https://go.googlesource.com/net", + "vcs": "git", + "revision": "45e771701b814666a7eb299e6c7a57d0b1799e91", + "branch": "master", + "path": "/trace", + "notests": true + }, { "importpath": "golang.org/x/tools/cmd/cover", "repository": "https://go.googlesource.com/tools", @@ -309,6 +510,15 @@ "path": "cover", "notests": true }, + { + "importpath": "google.golang.org/grpc/metadata", + "repository": "https://github.com/grpc/grpc-go", + "vcs": "git", + "revision": "9d682f9293b408c42d17c587d0e2a31237ac3f10", + "branch": "master", + "path": "/metadata", + "notests": true + }, { "importpath": "gopkg.in/DATA-DOG/go-sqlmock.v1", "repository": "https://gopkg.in/DATA-DOG/go-sqlmock.v1",