From accffcb325608731f8297503df55d9b6c190c883 Mon Sep 17 00:00:00 2001 From: Vadim Hleif Date: Fri, 21 Sep 2018 19:50:11 +0200 Subject: [PATCH] Migrate to readme yaml format (#7) * feat(README): migrate to readme yaml format * Update README and helm chart --- .gitignore | 1 + Dockerfile | 2 +- README.md | 144 +++++++++++++++++++++----- README.yaml | 184 ++++++++++++++++++++++++++++++++++ chart/Chart.yaml | 4 +- chart/charts/common-0.0.1.tgz | Bin 8298 -> 0 bytes chart/requirements.lock | 6 -- chart/requirements.yaml | 2 +- chart/values.yaml | 6 +- 9 files changed, 309 insertions(+), 40 deletions(-) create mode 100644 README.yaml delete mode 100644 chart/charts/common-0.0.1.tgz delete mode 100644 chart/requirements.lock diff --git a/.gitignore b/.gitignore index 6a959a9..ae473f9 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ *.iml .build-harness +build-harness dist/bin/* diff --git a/Dockerfile b/Dockerfile index 309fa8f..f72a989 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ COPY . . RUN go get && CGO_ENABLED=0 go build -v -o "./dist/bin/prometheus-to-cloudwatch" *.go -FROM alpine:3.7 +FROM alpine:3.8 RUN apk add --no-cache ca-certificates COPY --from=builder /go/src/github.com/cloudposse/prometheus-to-cloudwatch/dist/bin/prometheus-to-cloudwatch /usr/bin/prometheus-to-cloudwatch ENV PATH $PATH:/usr/bin diff --git a/README.md b/README.md index 6115f3f..f5d3b1f 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,41 @@ -# prometheus-to-cloudwatch [![Build Status](https://travis-ci.org/cloudposse/prometheus-to-cloudwatch.svg?branch=master)](https://travis-ci.org/cloudposse/prometheus-to-cloudwatch) + + +[![Cloud Posse](https://cloudposse.com/logo-300x69.svg)](https://cloudposse.com) + +# prometheus-to-cloudwatch [![Build Status](https://travis-ci.org/cloudposse/prometheus-to-cloudwatch.svg?branch=master)](https://travis-ci.org/cloudposse/prometheus-to-cloudwatch) [![Latest Release](https://img.shields.io/github/release/cloudposse/prometheus-to-cloudwatch.svg)](https://github.com/cloudposse/prometheus-to-cloudwatch/releases/latest) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) + + Utility for scraping Prometheus metrics from a Prometheus client endpoint and publishing them to CloudWatch +--- + +This project is part of our comprehensive ["SweetOps"](https://docs.cloudposse.com) approach towards DevOps. + + +It's 100% Open Source and licensed under the [APACHE2](LICENSE). + + + + + + + + +## Screenshots + + +![kube-state-metrics-service](images/kube-state-metrics-service.png) +*Kube state metrics* +![kube-state-metrics-to-cloudwatch](images/kube-state-metrics-to-cloudwatch.png) +*Kube state metrics to cloudwatch* + + + ## Usage + __NOTE__: The module accepts parameters as command-line arguments or as ENV variables (or any combination of command-line arguments and ENV vars). Command-line arguments take precedence over ENV vars @@ -32,10 +63,11 @@ which will provide IAM credentials to containers running inside a Kubernetes clu to publish metrics to CloudWatch. + + ## Examples ### Build Go program - ```sh go get @@ -108,10 +140,7 @@ Find the running services kubectl get services ``` -![kube-state-metrics-service](images/kube-state-metrics-service.png) - - -Copy the name of the `kube-state-metrics` service (`gauche-turtle-kube-state-metrics`) into the ENV var `PROMETHEUS_SCRAPE_URL` in [values.yaml](chart/values.yaml). +Copy the name of the `kube-state-metrics` service (e.g. `gauche-turtle-kube-state-metrics`) into the ENV var `PROMETHEUS_SCRAPE_URL` in [values.yaml](chart/values.yaml). It should look like this: ```sh @@ -131,12 +160,45 @@ helm install . ![kube-state-metrics-to-cloudwatch](images/kube-state-metrics-to-cloudwatch.png) + + + +## Related Projects + +Check out these related projects. + +- [Prometheus Operator](https://github.com/cloudposse/prometheus-operator) - Prometheus Operator creates/configures/manages Prometheus clusters atop Kubernetes +- [terraform-aws-cloudwatch-logs](https://github.com/cloudposse/terraform-aws-cloudwatch-logs) - Terraform Module to Provide a CloudWatch Logs Endpoint +- [terraform-aws-ecs-web-app](https://github.com/cloudposse/terraform-aws-ecs-web-app) - Terraform module that implements a web app on ECS and supports autoscaling, CI/CD, monitoring, ALB integration, and much more. + + + ## Help **Got a question?** -File a GitHub [issue](https://github.com/cloudposse/prometheus-to-cloudwatch/issues), send us an [email](mailto:hello@cloudposse.com) or reach out to us on [Gitter](https://gitter.im/cloudposse/). +File a GitHub [issue](https://github.com/cloudposse/prometheus-to-cloudwatch/issues), send us an [email][email] or join our [Slack Community][slack]. +## Commercial Support + +Work directly with our team of DevOps experts via email, slack, and video conferencing. + +We provide [*commercial support*][commercial_support] for all of our [Open Source][github] projects. As a *Dedicated Support* customer, you have access to our team of subject matter experts at a fraction of the cost of a full-time engineer. + +[![E-Mail](https://img.shields.io/badge/email-hello@cloudposse.com-blue.svg)](mailto:hello@cloudposse.com) + +- **Questions.** We'll use a Shared Slack channel between your team and ours. +- **Troubleshooting.** We'll help you triage why things aren't working. +- **Code Reviews.** We'll review your Pull Requests and provide constructive feedback. +- **Bug Fixes.** We'll rapidly work to fix any bugs in our projects. +- **Build New Terraform Modules.** We'll develop original modules to provision infrastructure. +- **Cloud Architecture.** We'll assist with your cloud strategy and design. +- **Implementation.** We'll provide hands-on support to implement our reference architectures. + + +## Community Forum + +Get access to our [Open Source Community Forum][slack] on Slack. It's **FREE** to join for everyone! Our "SweetOps" community is where you get to talk with others who share a similar vision for how to rollout and manage infrastructure. This is the best place to talk shop, ask questions, solicit feedback, and work together as a community to build *sweet* infrastructure. ## Contributing @@ -146,7 +208,7 @@ Please use the [issue tracker](https://github.com/cloudposse/prometheus-to-cloud ### Developing -If you are interested in being a contributor and want to get involved in developing `prometheus-to-cloudwatch`, we would love to hear from you! Shoot us an [email](mailto:hello@cloudposse.com). +If you are interested in being a contributor and want to get involved in developing this project or [help out](https://github.com/orgs/cloudposse/projects/3) with our other projects, we would love to hear from you! Shoot us an [email](mailto:hello@cloudposse.com). In general, PRs are welcome. We follow the typical "fork-and-pull" Git workflow. @@ -154,14 +216,20 @@ In general, PRs are welcome. We follow the typical "fork-and-pull" Git workflow. 2. **Clone** the project to your own machine 3. **Commit** changes to your own branch 4. **Push** your work back up to your fork - 5. Submit a **Pull request** so that we can review your changes + 5. Submit a **Pull Request** so that we can review your changes -**NOTE:** Be sure to merge the latest from "upstream" before making a pull request! +**NOTE:** Be sure to merge the latest changes from "upstream" before making a pull request! -## License +## Copyright -[APACHE 2.0](LICENSE) © 2018 [Cloud Posse, LLC](https://cloudposse.com) +Copyright © 2017-2018 [Cloud Posse, LLC](https://cloudposse.com) + + + +## License + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) See [LICENSE](LICENSE) for full details. @@ -173,7 +241,7 @@ See [LICENSE](LICENSE) for full details. "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an @@ -183,31 +251,53 @@ See [LICENSE](LICENSE) for full details. under the License. + + + + + + + +## Trademarks + +All other trademarks referenced herein are the property of their respective owners. + ## About -`prometheus-to-cloudwatch` is maintained and funded by [Cloud Posse, LLC][website]. +This project is maintained and funded by [Cloud Posse, LLC][website]. Like it? Please let us know at -![Cloud Posse](https://cloudposse.com/logo-300x69.png) +[![Cloud Posse](https://cloudposse.com/logo-300x69.svg)](https://cloudposse.com) +We're a [DevOps Professional Services][hire] company based in Los Angeles, CA. We love [Open Source Software](https://github.com/cloudposse/)! -Like it? Please let us know at +We offer paid support on all of our projects. -We love [Open Source Software](https://github.com/cloudposse/)! - -See [our other projects][community] -or [hire us][hire] to help build your next cloud platform. +Check out [our other projects][github], [apply for a job][jobs], or [hire us][hire] to help with your cloud strategy and implementation. + [docs]: https://docs.cloudposse.com/ [website]: https://cloudposse.com/ - [community]: https://github.com/cloudposse/ + [github]: https://github.com/cloudposse/ + [commercial_support]: https://github.com/orgs/cloudposse/projects + [jobs]: https://cloudposse.com/jobs/ [hire]: https://cloudposse.com/contact/ + [slack]: https://slack.cloudposse.com/ + [linkedin]: https://www.linkedin.com/company/cloudposse + [twitter]: https://twitter.com/cloudposse/ + [email]: mailto:hello@cloudposse.com ### Contributors -| [![Erik Osterman][erik_img]][erik_web]
[Erik Osterman][erik_web] | [![Andriy Knysh][andriy_img]][andriy_web]
[Andriy Knysh][andriy_web] | -|-------------------------------------------------------|------------------------------------------------------------------| +| [![Erik Osterman][osterman_avatar]][osterman_homepage]
[Erik Osterman][osterman_homepage] | [![Andriy Knysh][aknysh_avatar]][aknysh_homepage]
[Andriy Knysh][aknysh_homepage] | [![Igor Rodionov][goruha_avatar]][goruha_homepage]
[Igor Rodionov][goruha_homepage] | [![yufukui-m][yufukui-m_avatar]][yufukui-m_homepage]
[yufukui-m][yufukui-m_homepage] | +|---|---|---|---| + + [osterman_homepage]: https://github.com/osterman + [osterman_avatar]: https://github.com/osterman.png?size=150 + [aknysh_homepage]: https://github.com/aknysh + [aknysh_avatar]: https://github.com/aknysh.png?size=150 + [goruha_homepage]: https://github.com/goruha + [goruha_avatar]: https://github.com/goruha.png?size=150 + [yufukui-m_homepage]: https://github.com/yufukui-m + [yufukui-m_avatar]: https://github.com/yufukui-m.png?size=150 + - [erik_img]: http://s.gravatar.com/avatar/88c480d4f73b813904e00a5695a454cb?s=144 - [erik_web]: https://github.com/osterman/ - [andriy_img]: https://avatars0.githubusercontent.com/u/7356997?v=4&u=ed9ce1c9151d552d985bdf5546772e14ef7ab617&s=144 - [andriy_web]: https://github.com/aknysh/ diff --git a/README.yaml b/README.yaml new file mode 100644 index 0000000..63a2fde --- /dev/null +++ b/README.yaml @@ -0,0 +1,184 @@ +# Name of this project +name: prometheus-to-cloudwatch + +# License of this project +license: APACHE2 + +# Canonical GitHub repo +github_repo: cloudposse/prometheus-to-cloudwatch + +# Logo for this project +#logo: docs/logo.png + +# Badges to display +badges: + - name: "Build Status" + image: "https://travis-ci.org/cloudposse/prometheus-to-cloudwatch.svg?branch=master" + url: "https://travis-ci.org/cloudposse/prometheus-to-cloudwatch" + - name: "Latest Release" + image: "https://img.shields.io/github/release/cloudposse/prometheus-to-cloudwatch.svg" + url: "https://github.com/cloudposse/prometheus-to-cloudwatch/releases/latest" + - name: "Slack Community" + image: "https://slack.cloudposse.com/badge.svg" + url: "https://slack.cloudposse.com" + +related: + + - name: "Prometheus Operator" + description: "Prometheus Operator creates/configures/manages Prometheus clusters atop Kubernetes" + url: "https://github.com/cloudposse/prometheus-operator" + + - name: "terraform-aws-cloudwatch-logs" + description: "Terraform Module to Provide a CloudWatch Logs Endpoint" + url: "https://github.com/cloudposse/terraform-aws-cloudwatch-logs" + + - name: "terraform-aws-ecs-web-app" + description: "Terraform module that implements a web app on ECS and supports autoscaling, CI/CD, monitoring, ALB integration, and much more." + url: "https://github.com/cloudposse/terraform-aws-ecs-web-app" + +description: |- + + Utility for scraping Prometheus metrics from a Prometheus client endpoint and publishing them to CloudWatch + +usage: |- + + __NOTE__: The module accepts parameters as command-line arguments or as ENV variables (or any combination of command-line arguments and ENV vars). + Command-line arguments take precedence over ENV vars + + + | Command-line argument | ENV var | Description | + |:-----------------------------|:-----------------------------|:------------------------------------------------------------------------------| + | aws_access_key_id | AWS_ACCESS_KEY_ID | AWS access key Id with permissions to publish CloudWatch metrics | + | aws_secret_access_key | AWS_SECRET_ACCESS_KEY | AWS secret access key with permissions to publish CloudWatch metrics | + | cloudwatch_namespace | CLOUDWATCH_NAMESPACE | CloudWatch Namespace | + | cloudwatch_region | CLOUDWATCH_REGION | CloudWatch AWS Region | + | cloudwatch_publish_timeout | CLOUDWATCH_PUBLISH_TIMEOUT | CloudWatch publish timeout in seconds | + | prometheus_scrape_interval | PROMETHEUS_SCRAPE_INTERVAL | Prometheus scrape interval in seconds | + | prometheus_scrape_url | PROMETHEUS_SCRAPE_URL | The URL to scrape Prometheus metrics from | + | cert_path | CERT_PATH | Path to SSL Certificate file (when using SSL for `prometheus_scrape_url`) | + | keyPath | KEY_PATH | Path to Key file (when using SSL for `prometheus_scrape_url`) | + | accept_invalid_cert | ACCEPT_INVALID_CERT | Accept any certificate during TLS handshake. Insecure, use only for testing | + | additional_dimension | ADDITIONAL_DIMENSION | Additional dimension specified by NAME=VALUE | + + + __NOTE__: If AWS credentials are not provided in the command-line arguments (`aws_access_key_id` and `aws_secret_access_key`) + or ENV variables (`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`), + the chain of credential providers will search for credentials in the shared credential file and EC2 Instance Roles. + This is useful when deploying the module in AWS on Kubernetes with [`kube2iam`](https://github.com/jtblin/kube2iam), + which will provide IAM credentials to containers running inside a Kubernetes cluster, allowing the module to assume an IAM Role with permissions + to publish metrics to CloudWatch. + +examples: |- + ### Build Go program + ```sh + go get + + CGO_ENABLED=0 go build -v -o "./dist/bin/prometheus-to-cloudwatch" *.go + ``` + + + ### Run locally + + ```sh + export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXXXXX + export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + export CLOUDWATCH_NAMESPACE=kube-state-metrics + export CLOUDWATCH_REGION=us-east-1 + export CLOUDWATCH_PUBLISH_TIMEOUT=5 + export PROMETHEUS_SCRAPE_INTERVAL=30 + export PROMETHEUS_SCRAPE_URL=http://xxxxxxxxxxxx:8080/metrics + export CERT_PATH="" + export KEY_PATH="" + export ACCEPT_INVALID_CERT=true + + ./dist/bin/prometheus-to-cloudwatch + ``` + + + ### Build Docker image + __NOTE__: it will download all `Go` dependencies and then build the program inside the container (see [`Dockerfile`](Dockerfile)) + + + ```sh + docker build --tag prometheus-to-cloudwatch --no-cache=true . + ``` + + + ### Run in a Docker container + + ```sh + docker run -i --rm \ + -e AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXXXXX \ + -e AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \ + -e CLOUDWATCH_NAMESPACE=kube-state-metrics \ + -e CLOUDWATCH_REGION=us-east-1 \ + -e CLOUDWATCH_PUBLISH_TIMEOUT=5 \ + -e PROMETHEUS_SCRAPE_INTERVAL=30 \ + -e PROMETHEUS_SCRAPE_URL=http://xxxxxxxxxxxx:8080/metrics \ + -e CERT_PATH="" \ + -e KEY_PATH="" \ + -e ACCEPT_INVALID_CERT=true \ + prometheus-to-cloudwatch + ``` + + + ### Run on Kubernetes + + To run on `Kubernetes`, we will deploy two [`Helm`](https://helm.sh/) [charts](https://docs.helm.sh/developing_charts/) + + 1. [kube-state-metrics](https://github.com/kubernetes/charts/tree/master/stable/kube-state-metrics) - to generates metrics about the state of various objects inside the cluster, such as deployments, nodes and pods + + 2. [prometheus-to-cloudwatch](chart) - to scrape metrics from `kube-state-metrics` and publish them to CloudWatch + + Install `kube-state-metrics` chart + + ```sh + helm install stable/kube-state-metrics + ``` + + Find the running services + + ```sh + kubectl get services + ``` + + Copy the name of the `kube-state-metrics` service (e.g. `gauche-turtle-kube-state-metrics`) into the ENV var `PROMETHEUS_SCRAPE_URL` in [values.yaml](chart/values.yaml). + It should look like this: + + ```sh + PROMETHEUS_SCRAPE_URL: "http://gauche-turtle-kube-state-metrics:8080/metrics" + ``` + + Deploy `prometheus-to-cloudwatch` chart + + ```sh + cd chart + helm install . + ``` + + `prometheus-to-cloudwatch` will start scraping the `/metrics` endpoint of the `kube-state-metrics` service and send the Prometheus metrics to CloudWatch + + + ![kube-state-metrics-to-cloudwatch](images/kube-state-metrics-to-cloudwatch.png) + +screenshots: + +- name: "kube-state-metrics-service" + url: "images/kube-state-metrics-service.png" + description: "Kube state metrics" + +- name: "kube-state-metrics-to-cloudwatch" + url: "images/kube-state-metrics-to-cloudwatch.png" + description: "Kube state metrics to cloudwatch" + + +# Contributors to this project +contributors: + - name: "Erik Osterman" + github: "osterman" + - name: "Andriy Knysh" + github: "aknysh" + - name: "Igor Rodionov" + github: "goruha" + - name: "yufukui-m" + github: "yufukui-m" diff --git a/chart/Chart.yaml b/chart/Chart.yaml index b822ab8..b7e1f04 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 -appVersion: "1.0" +appVersion: 0.1.2 description: A Helm chart for prometheus-to-cloudwatch name: prometheus-to-cloudwatch -version: 0.1.0 +version: 0.1.2 diff --git a/chart/charts/common-0.0.1.tgz b/chart/charts/common-0.0.1.tgz deleted file mode 100644 index 06ede4fba6d9ad3fe350ff10bbb5af633e9b4e55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8298 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