├── 2019
├── 05-kubernetes-dashboard-gitlab
│ ├── README.md
│ ├── Todo.md
│ ├── ctl.sh
│ └── manifests
│ │ ├── kube-dashboard-ingress.yaml
│ │ ├── kube-dashboard-oauth2-proxy.yaml
│ │ ├── kube-dashboard-rbac.yaml
│ │ └── kube-dashboard.yaml
├── 08-k8s-python-operator
│ ├── Dockerfile
│ ├── LICENSE
│ ├── README.md
│ ├── copyrator
│ │ ├── __init__.py
│ │ ├── cli.py
│ │ ├── const.py
│ │ ├── load_crd.py
│ │ └── operator.py
│ ├── helm
│ │ ├── Chart.yaml
│ │ └── templates
│ │ │ ├── crd.yaml
│ │ │ ├── operator.yaml
│ │ │ └── rbac.yaml
│ └── setup.py
├── 09-cfs
│ ├── .gitignore
│ ├── apply
│ ├── infrastructure
│ │ ├── main.tf
│ │ └── main.yaml
│ └── tests
│ │ ├── ab-time-distribution.gp
│ │ ├── ab-timeline.gp
│ │ ├── generate-load-and-gather-results.yaml
│ │ ├── nginx-on-docker.yaml
│ │ └── nginx-on-docker
│ │ ├── nginx.conf
│ │ └── playbook.yaml
├── 09-http-bench
│ ├── .dockerignore
│ ├── Caddyfile
│ ├── Dockerfile
│ ├── Dockerfile.apache
│ ├── Dockerfile.caddy
│ ├── Makefile
│ ├── README.md
│ ├── app
│ │ └── benchmark.go
│ ├── bench.conf
│ └── nginx.conf
└── 10-remote-syslog
│ ├── .helm
│ ├── templates
│ │ └── 10-remote-syslog.yaml
│ └── values.yaml
│ ├── auditd-init.sh
│ ├── remote-syslog.sh
│ └── werf.yaml
├── 2020
├── 01-dynamic-build
│ ├── .gitlab-ci.yml
│ ├── .helm
│ │ ├── templates
│ │ │ ├── 10-app-dev.yaml
│ │ │ ├── 10-app.yaml
│ │ │ └── 20-ingress.yaml
│ │ └── values.yaml
│ ├── .werf
│ │ └── nginx.conf
│ ├── generate_artifacts
│ ├── get_git_history
│ └── werf.yml
├── 04-configmaps
│ ├── README.md
│ ├── charts
│ │ └── configmaps-demo
│ │ │ ├── .helmignore
│ │ │ ├── Chart.yaml
│ │ │ ├── templates
│ │ │ ├── _resources.tpl
│ │ │ ├── v1
│ │ │ │ ├── 06-cm-app.yaml
│ │ │ │ └── 20-app.yaml
│ │ │ ├── v2
│ │ │ │ ├── 06-cm-app.yaml
│ │ │ │ └── 20-app.yaml
│ │ │ ├── v3
│ │ │ │ ├── 06-cm-app.yaml
│ │ │ │ ├── 07-secret-app.yaml
│ │ │ │ └── 20-app.yaml
│ │ │ └── v4
│ │ │ │ ├── 06-cm-app.yaml
│ │ │ │ ├── 07-secret-app.yaml
│ │ │ │ └── 20-app.yaml
│ │ │ └── values.yaml
│ ├── images
│ │ └── configmaps-demo
│ │ │ └── Dockerfile
│ └── src
│ │ ├── configfiles
│ │ └── config.json
│ │ └── main.go
├── 04-etcdhelper
│ ├── README.md
│ ├── etcdhelper.go
│ ├── go.mod
│ └── go.sum
├── 08-k8s-raspberry-pi
│ ├── README.md
│ ├── cert-manager-cluster-issuer.yaml
│ ├── cert-manager-grafana-certificate.yaml
│ └── prometheus-pv.yaml
└── 08-kubecon
│ ├── README.md
│ ├── container
│ ├── Dockerfile
│ ├── ping_exporter.sh
│ └── secret_copier.sh
│ └── deployment.yaml
├── 2021
├── 01-cloudwatch-exporter
│ ├── .helm
│ │ ├── templates
│ │ │ ├── 01-config.yaml
│ │ │ ├── 20-cloudwatch-exporter.yaml
│ │ │ ├── 21-cost_exporter.yaml
│ │ │ └── 60-rules.yaml
│ │ └── values.yaml
│ ├── README.md
│ ├── policy.json
│ ├── role.json
│ ├── terraform_user_policy.tf
│ └── werf.yaml
├── 07-keycloak
│ ├── .helm
│ │ ├── Chart.yaml
│ │ ├── scripts
│ │ │ └── keycloak.cli
│ │ ├── templates
│ │ │ ├── NOTES.txt
│ │ │ ├── _envs.tpl
│ │ │ ├── _helpers.tpl
│ │ │ ├── _nodeselector.tpl
│ │ │ ├── _tolerations.tpl
│ │ │ ├── clusterrole.yaml
│ │ │ ├── clusterrolebinding.yaml
│ │ │ ├── configmap-startup.yaml
│ │ │ ├── infinispan-cm.yaml
│ │ │ ├── infinispan-sts.yaml
│ │ │ ├── ingress.yaml
│ │ │ ├── keycloak-cm.yaml
│ │ │ ├── keycloak-sts.yaml
│ │ │ ├── poddisruptionbudget.yaml
│ │ │ ├── rbac.yaml
│ │ │ ├── secrets.yaml
│ │ │ ├── service-headless.yaml
│ │ │ ├── service-http.yaml
│ │ │ ├── serviceaccount.yaml
│ │ │ └── servicemonitor.yaml
│ │ └── values.yaml
│ ├── README.md
│ ├── jar
│ │ ├── keycloak-metrics-spi-2.2.0.jar
│ │ ├── keycloak-model-jpa-12.0.4.jar
│ │ └── postgresql-42.2.19.jar
│ └── werf.yaml
├── 09-gitea-gitlab-migration
│ ├── README.md
│ ├── add_collaborator.py
│ ├── gitea_gitlab_import_repo.py
│ ├── gitea_gitlab_user_copier.py
│ ├── gitlab_fix_keys.py
│ ├── gitlab_fix_refs.py
│ └── requirements.txt
└── 09-memcached-mcrouter
│ ├── .helm
│ ├── templates
│ │ ├── mcrouter-cm.yaml
│ │ ├── mcrouter-ds.yaml
│ │ └── memcached-ds.yaml
│ └── values.yaml
│ ├── README.md
│ └── werf.yaml
├── 2022
├── 01-werf-local-dev
│ ├── .dockerignore
│ ├── .gitignore
│ ├── .helm
│ │ ├── Chart.lock
│ │ ├── Chart.yaml
│ │ ├── charts
│ │ │ ├── app
│ │ │ │ ├── Chart.yaml
│ │ │ │ └── templates
│ │ │ │ │ ├── _envs_app.tpl
│ │ │ │ │ ├── deployment.yaml
│ │ │ │ │ ├── ingress.yaml
│ │ │ │ │ ├── job-db-setup-and-migrate.yaml
│ │ │ │ │ ├── secret.yaml
│ │ │ │ │ └── service.yaml
│ │ │ └── mysql
│ │ │ │ ├── Chart.yaml
│ │ │ │ └── templates
│ │ │ │ ├── _envs_database.tpl
│ │ │ │ ├── mysql.yaml
│ │ │ │ ├── secret.yaml
│ │ │ │ └── service.yaml
│ │ ├── secret-values.yaml
│ │ └── values.yaml
│ ├── .prettierrc.yaml
│ ├── .sequelizerc
│ ├── .werf
│ │ └── nginx.conf
│ ├── Dockerfile
│ ├── README.md
│ ├── app.js
│ ├── bin
│ │ └── www
│ ├── config
│ │ └── database.js
│ ├── db
│ │ ├── migrations
│ │ │ └── 20211101064002-create-talker.js
│ │ └── models
│ │ │ ├── index.js
│ │ │ └── talker.js
│ ├── local
│ │ ├── deploy-app.sh
│ │ ├── info
│ │ │ ├── configure-docker.sh
│ │ │ ├── install-kubectl.sh
│ │ │ ├── install-minikube.sh
│ │ │ ├── install-werf.sh
│ │ │ └── registry.sh
│ │ ├── install.sh
│ │ ├── prepare.sh
│ │ ├── setup-infra.sh
│ │ ├── variables
│ │ └── yaml
│ │ │ └── registry-ingress.yaml
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── assets
│ │ │ ├── images
│ │ │ │ └── werf-logo.svg
│ │ │ ├── javascripts
│ │ │ │ ├── image.js
│ │ │ │ └── index.js
│ │ │ └── stylesheets
│ │ │ │ ├── image.css
│ │ │ │ └── style.css
│ │ └── pages
│ │ │ ├── image.html
│ │ │ └── index.html
│ ├── routes
│ │ ├── image.js
│ │ ├── index.js
│ │ ├── ping.js
│ │ └── talkers.js
│ ├── webpack.config.js
│ └── werf.yaml
├── 06-okteto
│ ├── Dockerfile
│ ├── README.md
│ ├── main.go
│ └── okteto.yml
├── 06-werf-kube-run
│ ├── Dockerfile
│ ├── README.md
│ ├── go.mod
│ ├── main.go
│ ├── main_test.go
│ └── werf.yaml
├── 06-werf-nodejs
│ ├── 01_basic_app
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── .helm
│ │ │ └── templates
│ │ │ │ ├── deployment.yaml
│ │ │ │ ├── ingress.yaml
│ │ │ │ └── service.yaml
│ │ ├── .prettierrc.yaml
│ │ ├── Dockerfile
│ │ ├── app.js
│ │ ├── bin
│ │ │ └── www
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── index.html
│ │ │ └── stylesheets
│ │ │ │ └── style.css
│ │ ├── routes
│ │ │ ├── index.js
│ │ │ └── ping.js
│ │ └── werf.yaml
│ ├── 020_assets
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── .helm
│ │ │ └── templates
│ │ │ │ ├── deployment.yaml
│ │ │ │ ├── ingress.yaml
│ │ │ │ └── service.yaml
│ │ ├── .prettierrc.yaml
│ │ ├── .werf
│ │ │ └── nginx.conf
│ │ ├── Dockerfile
│ │ ├── app.js
│ │ ├── bin
│ │ │ └── www
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── assets
│ │ │ │ ├── images
│ │ │ │ │ └── werf-logo.svg
│ │ │ │ ├── javascripts
│ │ │ │ │ ├── image.js
│ │ │ │ │ └── index.js
│ │ │ │ └── stylesheets
│ │ │ │ │ ├── image.css
│ │ │ │ │ └── style.css
│ │ │ └── pages
│ │ │ │ ├── image.html
│ │ │ │ └── index.html
│ │ ├── routes
│ │ │ ├── image.js
│ │ │ ├── index.js
│ │ │ └── ping.js
│ │ ├── webpack.config.js
│ │ └── werf.yaml
│ ├── 030_db
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── .helm
│ │ │ └── templates
│ │ │ │ ├── database.yaml
│ │ │ │ ├── deployment.yaml
│ │ │ │ ├── ingress.yaml
│ │ │ │ ├── job-db-setup-and-migrate.yaml
│ │ │ │ └── service.yaml
│ │ ├── .prettierrc.yaml
│ │ ├── .sequelizerc
│ │ ├── .werf
│ │ │ └── nginx.conf
│ │ ├── Dockerfile
│ │ ├── app.js
│ │ ├── bin
│ │ │ └── www
│ │ ├── config
│ │ │ └── database.json
│ │ ├── db
│ │ │ ├── migrations
│ │ │ │ └── 20211101064002-create-talker.js
│ │ │ └── models
│ │ │ │ ├── index.js
│ │ │ │ └── talker.js
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── assets
│ │ │ │ ├── images
│ │ │ │ │ └── werf-logo.svg
│ │ │ │ ├── javascripts
│ │ │ │ │ ├── image.js
│ │ │ │ │ └── index.js
│ │ │ │ └── stylesheets
│ │ │ │ │ ├── image.css
│ │ │ │ │ └── style.css
│ │ │ └── pages
│ │ │ │ ├── image.html
│ │ │ │ └── index.html
│ │ ├── routes
│ │ │ ├── image.js
│ │ │ ├── index.js
│ │ │ ├── ping.js
│ │ │ └── talkers.js
│ │ ├── webpack.config.js
│ │ └── werf.yaml
│ ├── 040_s3
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── .helm
│ │ │ └── templates
│ │ │ │ ├── database.yaml
│ │ │ │ ├── deployment.yaml
│ │ │ │ ├── ingress.yaml
│ │ │ │ ├── job-db-setup-and-migrate.yaml
│ │ │ │ ├── job-setup-minio.yaml
│ │ │ │ ├── service.yaml
│ │ │ │ └── storage.yaml
│ │ ├── .prettierrc.yaml
│ │ ├── .sequelizerc
│ │ ├── .werf
│ │ │ └── nginx.conf
│ │ ├── Dockerfile
│ │ ├── app.js
│ │ ├── bin
│ │ │ └── www
│ │ ├── config
│ │ │ ├── database.json
│ │ │ └── minio.json
│ │ ├── db
│ │ │ ├── migrations
│ │ │ │ └── 20211101064002-create-talker.js
│ │ │ └── models
│ │ │ │ ├── index.js
│ │ │ │ └── talker.js
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── assets
│ │ │ │ ├── images
│ │ │ │ │ └── werf-logo.svg
│ │ │ │ ├── javascripts
│ │ │ │ │ ├── image.js
│ │ │ │ │ └── index.js
│ │ │ │ └── stylesheets
│ │ │ │ │ ├── image.css
│ │ │ │ │ └── style.css
│ │ │ └── pages
│ │ │ │ ├── image.html
│ │ │ │ └── index.html
│ │ ├── routes
│ │ │ ├── files.js
│ │ │ ├── image.js
│ │ │ ├── index.js
│ │ │ ├── ping.js
│ │ │ └── talkers.js
│ │ ├── webpack.config.js
│ │ └── werf.yaml
│ └── README.md
├── 08-snapshot-controller
│ ├── 01-template-data
│ │ ├── 01-pvc.yaml
│ │ ├── 02-pod.yaml
│ │ ├── 03-clonned-pvc.yaml
│ │ └── 04-pods.yaml
│ ├── 02-upgrade-app
│ │ ├── 01-pvc.yaml
│ │ ├── 02-pod.yaml
│ │ ├── 03-clonned-pvc.yaml
│ │ ├── 04-pod.yaml
│ │ ├── 05-pod.yaml
│ │ ├── 06-snapshot.yaml
│ │ ├── 07-pod.yaml
│ │ ├── 08-snapshot.yaml
│ │ └── 09-restore-snapshot.yaml
│ ├── 03-backups-with-linstor
│ │ ├── 01-pvc.yaml
│ │ ├── 02-pod.yaml
│ │ ├── 03-volume-snapshot-class.yaml
│ │ ├── 04-snapshot.yaml
│ │ ├── 05-pvc.yaml
│ │ ├── 06-get-backups.sh
│ │ ├── 07-volumesnapshotcontent.yaml
│ │ ├── 08-volumesnapshot.yaml
│ │ ├── 09-pvc.yaml
│ │ └── minio
│ │ │ ├── 00-minio.yaml
│ │ │ └── 01-minio-connect.sh
│ └── README.md
├── 10-canary-example
│ ├── .gitlab-ci.yml
│ ├── .helm
│ │ ├── templates
│ │ │ ├── 10-nginx-config.yaml
│ │ │ ├── 20-nginx-deployment.yaml
│ │ │ └── 30-ingress.yaml
│ │ └── values.yaml
│ ├── README.md
│ ├── index.html
│ ├── werf-giterminism.yaml
│ └── werf.yaml
├── 11-d8-user-authn
│ ├── clean_up.sh
│ ├── dex-authenticator.yaml
│ ├── dex-provider.yaml
│ ├── dex-user.yaml
│ ├── echo-service.yaml
│ └── ldap.yaml
├── 11-redis-keydb
│ ├── Dockerfile
│ ├── README.md
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── 12-netflow
│ ├── .helm
│ ├── dashboards
│ │ ├── iptnetflow.json
│ │ └── nodegraph.json
│ ├── templates
│ │ ├── dashboard.yaml
│ │ ├── datasource.yaml
│ │ ├── deployment.yaml
│ │ ├── nodeconfig.yaml
│ │ ├── nodegraph.yaml
│ │ └── rbac.yaml
│ └── values.yaml
│ ├── go.mod
│ ├── go.sum
│ ├── main.go
│ ├── nodegraph
│ └── nodegraph.py
│ └── werf.yaml
├── 2023
├── 03-monitoring
│ ├── README.md
│ ├── alertmanager.yaml
│ ├── clean_up.sh
│ ├── custom-prometheus-rule.yaml
│ ├── grafana-additional-datasource.yaml
│ ├── grafana-dashboard-definition.yaml
│ ├── prometheus-remote-write.yaml
│ └── victoria-metrics.txt
├── 08-deckhouse-werf
│ ├── .gitignore
│ ├── .helm
│ │ └── templates
│ │ │ ├── database.yaml
│ │ │ ├── deployment.yaml
│ │ │ ├── ingress.yaml
│ │ │ ├── job-db-setup-and-migrate.yaml
│ │ │ └── service.yaml
│ ├── Dockerfile
│ ├── README.md
│ ├── cmd
│ │ └── main.go
│ ├── db
│ │ └── migrations
│ │ │ ├── 000001_create_talkers_table.down.sql
│ │ │ └── 000001_create_talkers_table.up.sql
│ ├── go.mod
│ ├── go.sum
│ ├── internal
│ │ ├── app
│ │ │ └── app.go
│ │ ├── common
│ │ │ └── json_logger_filter.go
│ │ ├── controllers
│ │ │ └── db_controllers.go
│ │ └── services
│ │ │ └── db_service.go
│ ├── templates
│ │ ├── index.html
│ │ ├── remember.html
│ │ └── say.html
│ └── werf.yaml
├── 08-werf-local-dev
│ ├── .configs
│ │ └── nginx.conf
│ ├── .gitignore
│ ├── .helm
│ │ └── templates
│ │ │ ├── database.yaml
│ │ │ ├── deployment-backend.yaml
│ │ │ ├── deployment-frontend.yaml
│ │ │ ├── ingress.yaml
│ │ │ ├── job-db-setup-and-migrate.yaml
│ │ │ └── service-backend.yaml
│ ├── README.md
│ ├── backend
│ │ ├── Dockerfile
│ │ ├── cmd
│ │ │ └── main.go
│ │ ├── db
│ │ │ └── migrations
│ │ │ │ ├── 000001_create_talkers_table.down.sql
│ │ │ │ └── 000001_create_talkers_table.up.sql
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── internal
│ │ │ ├── app
│ │ │ └── app.go
│ │ │ ├── common
│ │ │ └── json_logger_filter.go
│ │ │ ├── controllers
│ │ │ └── db_controllers.go
│ │ │ └── services
│ │ │ └── db_service.go
│ ├── docker-compose.yml
│ ├── frontend
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── babel.config.js
│ │ ├── jsconfig.json
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ └── index.html
│ │ ├── src
│ │ │ ├── App.vue
│ │ │ ├── assets
│ │ │ │ └── logo.png
│ │ │ ├── components
│ │ │ │ └── HelloWorld.vue
│ │ │ └── main.js
│ │ └── vue.config.js
│ ├── package-lock.json
│ └── werf.yaml
└── 11-github-actions
│ ├── .configs
│ └── nginx.conf
│ ├── .github
│ └── workflows
│ │ └── production_deployment.yml
│ ├── .gitignore
│ ├── .gitlab-ci.yml
│ ├── .helm
│ └── templates
│ │ ├── database.yaml
│ │ ├── deployment-backend.yaml
│ │ ├── deployment-frontend.yaml
│ │ ├── ingress.yaml
│ │ ├── job-db-setup-and-migrate.yaml
│ │ └── service-backend.yaml
│ ├── LICENSE
│ ├── backend
│ ├── Dockerfile
│ ├── cmd
│ │ └── main.go
│ ├── db
│ │ └── migrations
│ │ │ ├── 000001_create_talkers_table.down.sql
│ │ │ └── 000001_create_talkers_table.up.sql
│ ├── go.mod
│ ├── go.sum
│ └── internal
│ │ ├── app
│ │ └── app.go
│ │ ├── common
│ │ └── json_logger_filter.go
│ │ ├── controllers
│ │ └── db_controllers.go
│ │ └── services
│ │ └── db_service.go
│ ├── docker-compose.yml
│ ├── frontend
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── babel.config.js
│ ├── jsconfig.json
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ └── index.html
│ ├── src
│ │ ├── App.vue
│ │ ├── assets
│ │ │ └── logo.png
│ │ ├── components
│ │ │ └── HelloWorld.vue
│ │ └── main.js
│ └── vue.config.js
│ └── werf.yaml
├── 2024
├── 01-werf-deckhouse-gitlab
│ ├── .configs
│ │ └── nginx.conf
│ ├── .gitignore
│ ├── .gitlab-ci.yml
│ ├── .helm
│ │ └── templates
│ │ │ ├── database.yaml
│ │ │ ├── deployment-backend.yaml
│ │ │ ├── deployment-frontend.yaml
│ │ │ ├── ingress.yaml
│ │ │ ├── job-db-setup-and-migrate.yaml
│ │ │ ├── kube-pull-secret.yml
│ │ │ └── service-backend.yaml
│ ├── README.md
│ ├── backend
│ │ ├── Dockerfile
│ │ ├── cmd
│ │ │ └── main.go
│ │ ├── db
│ │ │ └── migrations
│ │ │ │ ├── 000001_create_talkers_table.down.sql
│ │ │ │ └── 000001_create_talkers_table.up.sql
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── internal
│ │ │ ├── app
│ │ │ └── app.go
│ │ │ ├── common
│ │ │ └── json_logger_filter.go
│ │ │ ├── controllers
│ │ │ └── db_controllers.go
│ │ │ └── services
│ │ │ └── db_service.go
│ ├── docker-compose.yml
│ ├── frontend
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── babel.config.js
│ │ ├── jsconfig.json
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ └── index.html
│ │ ├── src
│ │ │ ├── App.vue
│ │ │ ├── assets
│ │ │ │ └── logo.png
│ │ │ ├── components
│ │ │ │ └── HelloWorld.vue
│ │ │ └── main.js
│ │ └── vue.config.js
│ └── werf.yaml
└── 03-github-actions
│ ├── .configs
│ └── nginx.conf
│ ├── .github
│ └── workflows
│ │ └── production_deployment.yml
│ ├── .gitignore
│ ├── .helm
│ └── templates
│ │ ├── database.yaml
│ │ ├── deployment-backend.yaml
│ │ ├── deployment-frontend.yaml
│ │ ├── ingress.yaml
│ │ ├── job-db-setup-and-migrate.yaml
│ │ └── service-backend.yaml
│ ├── LICENSE
│ ├── README.md
│ ├── backend
│ ├── Dockerfile
│ ├── cmd
│ │ └── main.go
│ ├── db
│ │ └── migrations
│ │ │ ├── 000001_create_talkers_table.down.sql
│ │ │ └── 000001_create_talkers_table.up.sql
│ ├── go.mod
│ ├── go.sum
│ └── internal
│ │ ├── app
│ │ └── app.go
│ │ ├── common
│ │ └── json_logger_filter.go
│ │ ├── controllers
│ │ └── db_controllers.go
│ │ └── services
│ │ └── db_service.go
│ ├── docker-compose.yml
│ ├── frontend
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── babel.config.js
│ ├── jsconfig.json
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ └── index.html
│ ├── src
│ │ ├── App.vue
│ │ ├── assets
│ │ │ └── logo.png
│ │ ├── components
│ │ │ └── HelloWorld.vue
│ │ └── main.js
│ └── vue.config.js
│ └── werf.yaml
├── 2025
└── 02-dump-spb
│ └── README.md
├── .gitignore
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | **/.DS_Store
2 | .idea
3 |
--------------------------------------------------------------------------------
/2019/05-kubernetes-dashboard-gitlab/Todo.md:
--------------------------------------------------------------------------------
1 | * Добаваить health check в oauth2-proxy image
2 | * Права на чтение метрик хипстера, будет рисовать графики.
3 |
--------------------------------------------------------------------------------
/2019/08-k8s-python-operator/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.7.3-alpine3.9
2 |
3 | ADD . /app
4 |
5 | RUN pip3 install /app
6 |
7 | ENTRYPOINT ["copyrator"]
8 |
--------------------------------------------------------------------------------
/2019/08-k8s-python-operator/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Maksim Nabokikh
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/2019/08-k8s-python-operator/README.md:
--------------------------------------------------------------------------------
1 | k8s-python-operator-example
2 | ---------------------------
3 | Kubernetes operator written in Python.
4 |
5 | * «[Writing a Kubernetes Operator in Python without frameworks and SDK](https://blog.flant.com/writing-a-kubernetes-operator-in-python-without-frameworks-and-sdk/)».
6 | * Russian version: «[Kubernetes Operator на Python без фреймворков и SDK](https://habr.com/ru/company/flant/blog/459320/)».
7 |
8 |
9 | #### Launching the operator
10 | ```bash
11 | usage: copyrator [-h] [--namespace NAMESPACE] [--rule-name RULE_NAME]
12 |
13 | Copyrator - copy operator.
14 |
15 | optional arguments:
16 | -h, --help show this help message and exit
17 | --namespace NAMESPACE
18 | Operator Namespace (or ${NAMESPACE}), default: default
19 | --rule-name RULE_NAME
20 | CRD Name (or ${RULE_NAME}), default: main-rule
21 | ```
22 |
--------------------------------------------------------------------------------
/2019/08-k8s-python-operator/copyrator/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2019/08-k8s-python-operator/copyrator/__init__.py
--------------------------------------------------------------------------------
/2019/08-k8s-python-operator/copyrator/const.py:
--------------------------------------------------------------------------------
1 | # CRD Settings
2 | CRD_GROUP = 'flant.com'
3 | CRD_VERSION = 'v1'
4 | CRD_PLURAL = 'copyrators'
5 |
6 | # Type methods maps
7 | LIST_TYPES_MAP = {
8 | 'configmap': 'list_namespaced_config_map',
9 | 'secret': 'list_namespaced_secret',
10 | }
11 |
12 | CREATE_TYPES_MAP = {
13 | 'configmap': 'create_namespaced_config_map',
14 | 'secret': 'create_namespaced_secret',
15 | }
16 |
17 | # Allowed events
18 | ALLOWED_EVENT_TYPES = {'ADDED', 'UPDATED'}
19 |
--------------------------------------------------------------------------------
/2019/08-k8s-python-operator/copyrator/load_crd.py:
--------------------------------------------------------------------------------
1 | import kubernetes
2 |
3 | from copyrator.const import CRD_GROUP, CRD_VERSION, CRD_PLURAL
4 |
5 | __all__ = [
6 | 'load_crd',
7 | ]
8 |
9 |
10 | def load_crd(namespace, name):
11 | """
12 | Method for CRD loading.
13 | It is used to get the object's watching settings.
14 | """
15 | client = kubernetes.client.ApiClient()
16 | custom_api = kubernetes.client.CustomObjectsApi(client)
17 |
18 | crd = custom_api.get_namespaced_custom_object(
19 | CRD_GROUP,
20 | CRD_VERSION,
21 | namespace,
22 | CRD_PLURAL,
23 | name,
24 | )
25 | return {x: crd[x] for x in ('ruleType', 'selector', 'namespace')}
26 |
--------------------------------------------------------------------------------
/2019/08-k8s-python-operator/helm/Chart.yaml:
--------------------------------------------------------------------------------
1 | name: copyrator
2 | version: 0.0.1
3 |
--------------------------------------------------------------------------------
/2019/08-k8s-python-operator/helm/templates/crd.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apiextensions.k8s.io/v1beta1
2 | kind: CustomResourceDefinition
3 | metadata:
4 | name: copyrator.flant.com
5 | spec:
6 | group: flant.com
7 | versions:
8 | - name: v1
9 | served: true
10 | storage: true
11 | scope: Namespaced
12 | names:
13 | plural: copyrators
14 | singular: copyrator
15 | kind: CopyratorRule
16 | shortNames:
17 | - copyr
18 | validation:
19 | openAPIV3Schema:
20 | type: object
21 | properties:
22 | ruleType:
23 | type: string
24 | namespace:
25 | type: string
26 | selector:
27 | type: object
28 |
--------------------------------------------------------------------------------
/2019/08-k8s-python-operator/helm/templates/operator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: {{ .Chart.Name }}
5 | spec:
6 | selector:
7 | matchLabels:
8 | name: {{ .Chart.Name }}
9 | template:
10 | metadata:
11 | labels:
12 | name: {{ .Chart.Name }}
13 | spec:
14 | containers:
15 | - name: {{ .Chart.Name }}
16 | image: privaterepo.yourcompany.com/copyrator:latest
17 | imagePullPolicy: Always
18 | args: ["--rule-type", "main-rule"]
19 | env:
20 | - name: NAMESPACE
21 | valueFrom:
22 | fieldRef:
23 | fieldPath: metadata.namespace
24 | serviceAccountName: {{ .Chart.Name }}-acc
25 |
--------------------------------------------------------------------------------
/2019/08-k8s-python-operator/helm/templates/rbac.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: {{ .Chart.Name }}-acc
6 |
7 | ---
8 | apiVersion: rbac.authorization.k8s.io/v1beta1
9 | kind: ClusterRole
10 | metadata:
11 | name: {{ .Chart.Name }}
12 | rules:
13 | - apiGroups: [""]
14 | resources: ["namespaces"]
15 | verbs: ["get", "watch", "list"]
16 | - apiGroups: [""]
17 | resources: ["secrets", "configmaps"]
18 | verbs: ["*"]
19 | ---
20 | apiVersion: rbac.authorization.k8s.io/v1beta1
21 | kind: ClusterRoleBinding
22 | metadata:
23 | name: {{ .Chart.Name }}
24 | roleRef:
25 | apiGroup: rbac.authorization.k8s.io
26 | kind: ClusterRole
27 | name: {{ .Chart.Name }}
28 | subjects:
29 | - kind: ServiceAccount
30 | name: {{ .Chart.Name }}-acc
31 |
--------------------------------------------------------------------------------
/2019/08-k8s-python-operator/setup.py:
--------------------------------------------------------------------------------
1 | from sys import version_info
2 |
3 | from setuptools import find_packages, setup
4 |
5 | if version_info[:2] < (3, 5):
6 | raise RuntimeError(
7 | 'Unsupported python version %s.' % '.'.join(version_info)
8 | )
9 |
10 |
11 | _NAME = 'copyrator'
12 | setup(
13 | name=_NAME,
14 | version='0.0.1',
15 | packages=find_packages(),
16 | classifiers=[
17 | 'Development Status :: 3 - Alpha',
18 | 'Programming Language :: Python',
19 | 'Programming Language :: Python :: 3',
20 | 'Programming Language :: Python :: 3.5',
21 | 'Programming Language :: Python :: 3.6',
22 | 'Programming Language :: Python :: 3.7',
23 | ],
24 | author='Flant',
25 | author_email='maksim.nabokikh@flant.com',
26 | include_package_data=True,
27 | install_requires=[
28 | 'kubernetes==9.0.0',
29 | ],
30 | entry_points={
31 | 'console_scripts': [
32 | '{0} = {0}.cli:main'.format(_NAME),
33 | ]
34 | }
35 | )
36 |
--------------------------------------------------------------------------------
/2019/09-cfs/.gitignore:
--------------------------------------------------------------------------------
1 | .terraform
2 | terraform.tfstate
3 | terraform.tfstate.backup
4 | results
5 |
--------------------------------------------------------------------------------
/2019/09-cfs/apply:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | export ANSIBLE_SSH_ARGS="${ANSIBLE_SSH_ARGS:-"-C -o ControlMaster=auto -o ControlPersist=600s"} -o StrictHostKeyChecking=accept-new"
6 |
7 | if [[ "x$OS_X_JUMPER_HOST" != "x" ]] ; then
8 | export ANSIBLE_SSH_ARGS="${ANSIBLE_SSH_ARGS} -o ProxyCommand='ssh ${OS_X_JUMPER_USER:-$USER}@$OS_X_JUMPER_HOST -W %h:%p'"
9 |
10 | export TF_VAR_ssh_bastion_host=$OS_X_JUMPER_HOST
11 | export TF_VAR_ssh_bastion_user=${OS_X_JUMPER_USER:-$USER}
12 | fi
13 |
14 | eval $(ssh-agent) > /dev/null
15 | trap "kill $SSH_AGENT_PID" EXIT
16 | ssh-add ~/.ssh/id_rsa || exit $?
17 |
18 | if [ ! -d .terraform ] ; then
19 | terraform init infrastructure
20 | fi
21 | terraform apply infrastructure
22 |
23 | ansible-playbook -i ~/.ansible-terraform-inventory infrastructure/main.yaml
24 |
--------------------------------------------------------------------------------
/2019/09-cfs/infrastructure/main.yaml:
--------------------------------------------------------------------------------
1 | - hosts: nginx-on-docker
2 | tasks:
3 | - name: install docker
4 | apt:
5 | name: docker.io
6 | update_cache: yes
7 | - name: install python3 and pip3
8 | apt:
9 | name:
10 | - python3
11 | - python3-pip
12 | - name: install docker-py
13 | pip:
14 | name: docker-py
15 |
16 | - hosts: load-generator
17 | tasks:
18 | - name: install apache benchmark and gnuplot
19 | apt:
20 | name:
21 | - apache2-utils
22 | - gnuplot
23 | update_cache: yes
24 | - name: add nginx-on-docker to /etc/hosts
25 | lineinfile:
26 | dest: /etc/hosts
27 | regexp: '^{{ hostvars["nginx-on-docker"].ansible_default_ipv4.address }} nginx-on-docker$'
28 | line: '{{ hostvars["nginx-on-docker"].ansible_default_ipv4.address }} nginx-on-docker'
29 | state: present
30 |
--------------------------------------------------------------------------------
/2019/09-cfs/tests/ab-time-distribution.gp:
--------------------------------------------------------------------------------
1 | # Let's output to a jpeg file
2 | set terminal jpeg size 1920,1080
3 | # This sets the aspect ratio of the graph
4 | set size 1, 1
5 | # The file we'll write to
6 | set output "time-distribution.jpg"
7 | # The graph title
8 | set title "Benchmark testing"
9 | # Where to place the legend/key
10 | set key left top
11 | # Draw gridlines oriented on the y axis
12 | set grid y
13 | # Label the x-axis
14 | set xlabel 'requests'
15 | # Label the y-axis
16 | set ylabel "response time (ms)"
17 | # Tell gnuplot to use tabs as the delimiter instead of spaces (default)
18 | set datafile separator '\t'
19 | # Plot the data
20 | plot "result.tsv" every ::2 using 5 title 'response time' with lines
21 | exit
22 |
--------------------------------------------------------------------------------
/2019/09-cfs/tests/ab-timeline.gp:
--------------------------------------------------------------------------------
1 | # Let's output to a jpeg file
2 | set terminal jpeg size 1920,1080
3 | # This sets the aspect ratio of the graph
4 | set size 1, 1
5 | # The file we'll write to
6 | set output "timeline.jpg"
7 | # The graph title
8 | set title "Benchmark testing"
9 | # Where to place the legend/key
10 | set key left top
11 | # Draw gridlines oriented on the y axis
12 | set grid y
13 | # Specify that the x-series data is time data
14 | set xdata time
15 | # Specify the *input* format of the time data
16 | set timefmt "%s"
17 | # Specify the *output* format for the x-axis tick labels
18 | set format x "%S"
19 | # Label the x-axis
20 | set xlabel 'seconds'
21 | # Label the y-axis
22 | set ylabel "response time (ms)"
23 | # Tell gnuplot to use tabs as the delimiter instead of spaces (default)
24 | set datafile separator '\t'
25 | # Plot the data
26 | plot "result.tsv" every ::2 using 2:5 title 'response time' with points
27 | exit
28 |
--------------------------------------------------------------------------------
/2019/09-cfs/tests/generate-load-and-gather-results.yaml:
--------------------------------------------------------------------------------
1 | - hosts: load-generator
2 | gather_facts: no
3 | vars:
4 | ab_plots:
5 | - time-distribution
6 | - timeline
7 | tasks:
8 | - name: run ab
9 | command: "ab -g result.tsv {{ ab_params }}"
10 | register: ab_output
11 | - name: save ab output
12 | delegate_to: localhost
13 | copy:
14 | content: "{{ ab_output.stdout }}"
15 | dest: "../results/{{ test_result_path }}/ab.txt"
16 | - name: setup gnuplot
17 | copy:
18 | src: "ab-{{ item }}.gp"
19 | dest: "ab-{{ item }}.gp"
20 | loop: "{{ ab_plots }}"
21 | - name: generate plot
22 | command: "gnuplot ab-{{ item }}.gp"
23 | loop: "{{ ab_plots }}"
24 | - name: fetch plot
25 | fetch:
26 | src: "{{ item }}.jpg"
27 | dest: "../results/{{ test_result_path }}/{{ item }}.jpg"
28 | flat: true
29 | loop: "{{ ab_plots }}"
30 |
--------------------------------------------------------------------------------
/2019/09-cfs/tests/nginx-on-docker/nginx.conf:
--------------------------------------------------------------------------------
1 | user nginx;
2 | worker_processes 4;
3 |
4 | error_log /var/log/nginx/error.log warn;
5 | pid /var/run/nginx.pid;
6 |
7 |
8 | events {
9 | worker_connections 16384;
10 | }
11 |
12 |
13 | http {
14 | include /etc/nginx/mime.types;
15 | default_type application/octet-stream;
16 |
17 | access_log off;
18 |
19 | sendfile on;
20 |
21 | keepalive_timeout 65;
22 |
23 | server {
24 | listen 80;
25 | server_name localhost;
26 |
27 | location / {
28 | root /usr/share/nginx/html;
29 | index index.html index.htm;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/2019/09-http-bench/.dockerignore:
--------------------------------------------------------------------------------
1 | Makefile
2 |
3 | nginx-selfsigned.key
4 | nginx-selfsigned.crt
5 |
6 | app/benchmark
--------------------------------------------------------------------------------
/2019/09-http-bench/Caddyfile:
--------------------------------------------------------------------------------
1 | http://benchmark.test, https://benchmark.test {
2 |
3 | tls /etc/ssl/certs/nginx-selfsigned.crt /etc/ssl/private/nginx-selfsigned.key
4 |
5 | status 200 /bench
6 | }
--------------------------------------------------------------------------------
/2019/09-http-bench/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | docker build -t benchmark:v1 .
3 |
4 | build-apache:
5 | docker build -f Dockerfile.apache -t benchmark-apache:v1 .
6 |
7 | build-caddy:
8 | docker build -f Dockerfile.caddy -t benchmark-caddy:v1 .
9 |
10 |
11 | run: build
12 | docker run --rm -d --add-host=benchmark.test:127.0.0.1 --name go_benchmark benchmark:v1
13 |
14 | run-apache: build-apache
15 | docker run --rm -d --add-host=benchmark.test:127.0.0.1 --name go_benchmark benchmark-apache:v1
16 |
17 | run-caddy: build-caddy
18 | docker run --rm -d --add-host=benchmark.test:127.0.0.1 --name go_benchmark benchmark-caddy:v1
19 |
20 |
21 | bench: run
22 | docker exec -it go_benchmark ./benchmark -u https://benchmark.test/bench -c 1000 -n 10000
23 | docker rm -f go_benchmark 2>&1 1>/dev/null
24 |
25 | bench-apache: run-apache
26 | docker exec -it go_benchmark ./benchmark -u https://benchmark.test/bench -c 1000 -n 10000
27 | docker rm -f go_benchmark 2>&1 1>/dev/null
28 |
29 | bench-caddy: run-caddy
30 | docker exec -it go_benchmark ./benchmark -u https://benchmark.test/bench -c 1000 -n 10000
31 | docker rm -f go_benchmark 2>&1 1>/dev/null
--------------------------------------------------------------------------------
/2019/09-http-bench/README.md:
--------------------------------------------------------------------------------
1 | This repo demonstrates how a simple benchmark (written in Golang and built into Docker container) works against your HTTP server. More details are described in our [6 recent cases from our SRE workaday routine](https://blog.flant.com/troubleshooting-web-apps-issues-6-recent-cases-from-our-sres/) article (case #1).
2 |
3 | # How to use it
4 |
5 | Clone this repo and execute:
6 |
7 | ```shell
8 | make bench
9 | ```
10 | … or do everything step by step:
11 |
12 | 1) Build your image:
13 | ```shell
14 | docker build -t benchmark:v1 .
15 | ```
16 |
17 | 2) Start a container:
18 | ```shell
19 | docker run --rm -d --add-host=benchmark.test:127.0.0.1 --name go_benchmark benchmark:v1
20 | ```
21 |
22 | 3) Run the benchmark for it:
23 | ```shell
24 | docker exec -it go_benchmark ./benchmark -u https://benchmark.test/bench -c 100 -n 100000
25 | ```
26 |
--------------------------------------------------------------------------------
/2019/09-http-bench/bench.conf:
--------------------------------------------------------------------------------
1 | Listen 443
2 |
3 | Protocols h2 h2c http/1.1
4 | DocumentRoot "/usr/local/apache2/htdocs/"
5 | ServerName bench.test
6 |
7 |
8 |
9 | Protocols h2 h2c http/1.1
10 | SSLEngine on
11 | SSLCertificateFile /etc/ssl/certs/nginx-selfsigned.crt
12 | SSLCertificateKeyFile /etc/ssl/private/nginx-selfsigned.key
13 | DocumentRoot "/usr/local/apache2/htdocs/"
14 | ServerName bench.test
15 |
16 |
--------------------------------------------------------------------------------
/2019/10-remote-syslog/.helm/values.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | syslog_server:
3 | default: "10.10.10.10:514"
4 |
--------------------------------------------------------------------------------
/2019/10-remote-syslog/werf.yaml:
--------------------------------------------------------------------------------
1 | project: remote-syslog
2 | configVersion: 1
3 | ---
4 | image: remote-syslog
5 | from: ubuntu:16.04
6 | git:
7 | - add: /
8 | to: /opt/scripts/
9 | ---
10 | image: auditd-init
11 | from: ubuntu:16.04
12 |
13 | mount:
14 | - from: tmp_dir
15 | to: /var/lib/apt/lists
16 | - from: build_dir
17 | to: /var/cache/apt
18 |
19 | git:
20 | - add: /
21 | to: /opt/scripts/
22 |
23 | ansible:
24 | install:
25 | - name: Install wget
26 | apt:
27 | update_cache: yes
28 | name: wget
29 | state: latest
--------------------------------------------------------------------------------
/2020/01-dynamic-build/.helm/values.yaml:
--------------------------------------------------------------------------------
1 | host:
2 | production: werf.io
3 | _default: werf.test.flant.com
4 |
5 | basic_auth: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 |
7 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/.helmignore:
--------------------------------------------------------------------------------
1 |
2 | # Patterns to ignore when building packages.
3 | # This supports shell glob matching, relative path matching, and
4 | # negation (prefixed with !). Only one pattern per line.
5 | .DS_Store
6 | # Common VCS dirs
7 | .git/
8 | .gitignore
9 | .bzr/
10 | .bzrignore
11 | .hg/
12 | .hgignore
13 | .svn/
14 | # Common backup files
15 | *.swp
16 | *.bak
17 | *.tmp
18 | *~
19 | # Various IDEs
20 | .project
21 | .idea/
22 | *.tmproj
23 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/Chart.yaml:
--------------------------------------------------------------------------------
1 | name: configmaps-demo
2 | version: 0.0.1
3 | description: Configmap usage example
4 | keywords:
5 | - demo
6 | - configmap
7 | home: https://github.com/flant/examples.git
8 | maintainers:
9 | - name: Mikhail Nosov
10 | email: drdeimosnn@gmail.com
11 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/templates/_resources.tpl:
--------------------------------------------------------------------------------
1 | # vi:syntax=yaml
2 | # vi:filetype=yaml
3 |
4 | {{- define "resources_app" }}
5 | resources:
6 | requests:
7 | memory: {{ .Values.resources.app.requests.memory | quote }}
8 | cpu: {{ .Values.resources.app.requests.cpu | quote }}
9 | limits:
10 | memory: {{ .Values.resources.app.limits.memory | quote }}
11 | {{- end }}
12 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/templates/v1/06-cm-app.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: {{ printf "%s-app-v1" .Chart.Name | quote }}
5 | data:
6 | config.json: |
7 | {
8 | "welcome": {{ pluck .Values.global.env .Values.welcome | first | quote }},
9 | "name": {{ pluck .Values.global.env .Values.name | first | quote }}
10 | }
11 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/templates/v1/20-app.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Deployment
3 | metadata:
4 | name: {{ printf "%s-v1" .Chart.Name | quote }}
5 | spec:
6 | revisionHistoryLimit: 2
7 | replicas: 1
8 | template:
9 | metadata:
10 | labels:
11 | app: {{ printf "%s-v1" .Chart.Name | quote }}
12 | spec:
13 | imagePullSecrets:
14 | - name: "registrysecret"
15 | volumes:
16 | - name: app-conf
17 | configMap:
18 | name: {{ printf "%s-app-v1" .Chart.Name | quote }}
19 | containers:
20 | - name: {{ .Chart.Name | quote }}
21 | {{- include "resources_app" . | indent 8 }}
22 | image: "drdeimosnn/configmaps-demo:0.0.1"
23 | imagePullPolicy: Always
24 | securityContext:
25 | runAsUser: 40004
26 | allowPrivilegeEscalation: false
27 | command: [ "/bin/configmaps-demo" ]
28 | workingDir: "/app"
29 | volumeMounts:
30 | - name: app-conf
31 | mountPath: /app/configfiles/config.json
32 | subPath: config.json
33 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/templates/v2/06-cm-app.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: {{ printf "%s-app-v2-changeme" .Chart.Name | quote }}
5 | data:
6 | config.json: |
7 | {
8 | "welcome": {{ pluck .Values.global.env .Values.welcome | first | quote }},
9 | "name": {{ pluck .Values.global.env .Values.name | first | quote }}
10 | }
11 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/templates/v2/20-app.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Deployment
3 | metadata:
4 | name: {{ printf "%s-v2-removemeafterchange" .Chart.Name | quote }}
5 | spec:
6 | revisionHistoryLimit: 2
7 | replicas: 1
8 | template:
9 | metadata:
10 | labels:
11 | app: {{ printf "%s-v2" .Chart.Name | quote }}
12 | spec:
13 | imagePullSecrets:
14 | - name: "registrysecret"
15 | volumes:
16 | - name: app-conf
17 | configMap:
18 | name: {{ printf "%s-app-v2-changeme" .Chart.Name | quote }}
19 | containers:
20 | - name: {{ .Chart.Name | quote }}
21 | {{- include "resources_app" . | indent 8 }}
22 | image: "drdeimosnn/configmaps-demo:0.0.1"
23 | imagePullPolicy: Always
24 | securityContext:
25 | runAsUser: 40004
26 | allowPrivilegeEscalation: false
27 | command: [ "/bin/configmaps-demo" ]
28 | workingDir: "/app"
29 | volumeMounts:
30 | - name: app-conf
31 | mountPath: /app/configfiles/config.json
32 | subPath: config.json
33 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/templates/v3/06-cm-app.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: {{ printf "%s-app-v3" .Chart.Name | quote }}
5 | data:
6 | config.json: |
7 | {
8 | "welcome": {{ pluck .Values.global.env .Values.welcome | first | quote }},
9 | "name": {{ pluck .Values.global.env .Values.name | first | quote }}
10 | }
11 | database.yml: |
12 | host: 127.0.0.1
13 | db: app
14 | user: app
15 | password: app
16 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/templates/v3/07-secret-app.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: {{ printf "%s-app-secret-v3" .Chart.Name | quote }}
5 | type: Opaque
6 | data:
7 | secret_conf.json: {{ .Values.secret.secret_conf_json | b64enc }}
8 | another.yaml: {{ .Values.secret.another_yaml | b64enc }}
9 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/templates/v4/06-cm-app.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: {{ printf "%s-app-v4" .Chart.Name | quote }}
5 | data:
6 | config.json: |
7 | {
8 | "welcome": {{ pluck .Values.global.env .Values.welcome | first | quote }},
9 | "name": {{ pluck .Values.global.env .Values.name | first | quote }}
10 | }
11 | database.yml: |
12 | host: 127.0.0.1
13 | db: app
14 | user: app
15 | password: app
16 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/templates/v4/07-secret-app.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: {{ printf "%s-app-secret-v4" .Chart.Name | quote }}
5 | type: Opaque
6 | data:
7 | secret_conf.json: {{ .Values.secret.secret_conf_json | b64enc }}
8 | another.yaml: {{ .Values.secret.another_yaml | b64enc }}
9 |
--------------------------------------------------------------------------------
/2020/04-configmaps/charts/configmaps-demo/values.yaml:
--------------------------------------------------------------------------------
1 | welcome:
2 | production: "Hello"
3 | name:
4 | production: "Bob"
5 |
6 | secret:
7 | secret_conf_json: |
8 | {
9 | "bob_key": "SuperSecretKeyV2",
10 | "alice_key": "AnotherSuperSecretKeyV2"
11 | }
12 | another_yaml: |
13 | external:
14 | service:
15 | access_key: "MoreSecretsMoar!"
16 |
17 | resources:
18 | app:
19 | requests:
20 | cpu: "50m"
21 | memory: "64Mi"
22 | limits:
23 | memory: "64Mi"
24 |
--------------------------------------------------------------------------------
/2020/04-configmaps/images/configmaps-demo/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.13-alpine
2 |
3 | RUN adduser -u 40004 -h /app -D app
4 |
5 | RUN mkdir /app/configfiles /app/secretfiles && \
6 | chown -R app:app /app/configfiles /app/secretfiles
7 |
8 | RUN apk update && apk add git
9 |
10 | RUN go get github.com/fsnotify/fsnotify
11 |
12 | RUN cd /go/src/github.com && \
13 | git clone https://github.com/flant/examples.git flant-examples
14 |
15 | RUN cd /go/src/github.com/flant-examples/2020/04-configmaps/src/ && \
16 | go build -o /bin/configmaps-demo main.go
17 |
--------------------------------------------------------------------------------
/2020/04-configmaps/src/configfiles/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcome": "Hello",
3 | "name": "Alice"
4 | }
5 |
--------------------------------------------------------------------------------
/2020/08-k8s-raspberry-pi/README.md:
--------------------------------------------------------------------------------
1 | # Running Kubernetes on Raspberry Pi
2 |
3 | Here are example YAML manifests to simplify deploying basic components for Kubernetes cluster installed on Raspberry Pi.
4 |
5 | This repo is intended to be used as a playground for a corresponding article:
6 |
7 | * «**[Installing fully-fledged vanilla Kubernetes on Raspberry Pi](https://blog.flant.com/installing-fully-fledged-vanilla-kubernetes-on-raspberry-pi/)**».
8 | * Russian version: «[Полноценный Kubernetes с нуля на Raspberry Pi](https://habr.com/ru/company/flant/blog/513908/)».
9 |
10 | ## Contents
11 |
12 | * `prometheus-pv.yaml` — PVs (persistent volumes) for a simple local storage (based on hostpath) for Prometheus
13 | (including AlertManager data);
14 | * `cert-manager-cluster-issuer.yaml` & `cert-manager-grafana-certificate.yaml` — cert-manager's cluster issuer & SSL
15 | certificate for Grafana.
16 |
--------------------------------------------------------------------------------
/2020/08-k8s-raspberry-pi/cert-manager-cluster-issuer.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1alpha2
2 | kind: ClusterIssuer
3 | metadata:
4 | name: letsencrypt-staging
5 | spec:
6 | acme:
7 | # You must replace this email address with your own.
8 | # Let's Encrypt will use this to contact you about expiring
9 | # certificates, and issues related to your account.
10 | email: raspberry-pi@home.pi
11 | server: https://acme-staging-v02.api.letsencrypt.org/directory
12 | privateKeySecretRef:
13 | # Secret resource that will be used to store the account's private key.
14 | name: example-issuer-account-key
15 | # Add a single challenge solver, HTTP01 using nginx
16 | solvers:
17 | - http01:
18 | ingress:
19 | class: nginx
20 |
--------------------------------------------------------------------------------
/2020/08-k8s-raspberry-pi/cert-manager-grafana-certificate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1alpha2
2 | kind: Certificate
3 | metadata:
4 | name: grafana
5 | namespace: monitoring
6 | spec:
7 | dnsNames:
8 | - grafana.home.pi
9 | secretName: grafana-tls
10 | issuerRef:
11 | kind: ClusterIssuer
12 | name: letsencrypt-staging
13 |
14 |
--------------------------------------------------------------------------------
/2020/08-kubecon/README.md:
--------------------------------------------------------------------------------
1 | # shell-operator examples from KubeCon Europe 2020
2 |
3 | Here are some scripts & manifests mentioned in the «[Go? Bash! Meet the Shell-operator](https://kccnceu20.sched.com/event/cfea816a0ae640df39193f45baf18cc1)»
4 | talk by [Flant](https://flant.com/) during KubeCon + CloudNativeCon Europe 2020.
5 |
6 | You might be also interested in:
7 | * watching the [full video](https://www.youtube.com/watch?v=we0s4ETUBLc) of this talk;
8 | * reading a [shorter article](https://blog.flant.com/go-bash-meet-the-shell-operator/) based on this talk
9 | (it has [its Russian version](https://habr.com/ru/company/flant/blog/519208/) as well);
10 | * downloading the [slides](https://speakerdeck.com/flant/go-bash-meet-the-shell-operator);
11 | * checking [shell-operator](https://github.com/flant/shell-operator) and [addon-operator](https://github.com/flant/addon-operator) GitHub repos.
12 |
13 | ## Contents
14 |
15 | * `secrets_copier.sh` is a Bash implementation for copying Secrets using shell-operator (example 0);
16 | * `ping_exporter.sh` is a simple (ping-based) network monitoring using shell-operator (example 3).
17 |
--------------------------------------------------------------------------------
/2020/08-kubecon/container/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM flant/shell-operator:v1.0.0-beta.12-alpine3.11
2 |
3 | COPY 2020/08-kubecon/container/secret_copier.sh /hooks/
4 | COPY 2020/08-kubecon/container/ping_exporter.sh /hooks/
5 |
--------------------------------------------------------------------------------
/2021/01-cloudwatch-exporter/.helm/values.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | aws_access_key_id: PRODPRODPRODPRODPROD
3 | aws_secret_access_key: eideikuoBaeg1ohNeeyiezeej1Boeyo4
4 |
5 | region:
6 | _default: eu-central-1
7 | prod: eu-central-1
8 |
9 | replicas:
10 | _default: 1
11 |
12 | resources:
13 | requests:
14 | cpu:
15 | _default: 1m
16 | memory:
17 | _default: 256Mi
18 |
19 | env:
20 | metric_today_daily_costs: "yes"
21 | metric_yesterday_daily_costs: "yes"
22 | query_period: "1800"
23 | metric_today_daily_usage: "yes"
24 | metric_today_daily_usage_norm: "yes"
25 |
--------------------------------------------------------------------------------
/2021/01-cloudwatch-exporter/README.md:
--------------------------------------------------------------------------------
1 | # Using Prometheus exporters with AWS CloudWatch
2 |
3 | Here are Kubernetes manifests to deploy cloudwatch_exporter and prometheus_aws_cost_exporter in Kubernetes for getting AWS CloudWatch metrics in Prometheus.
4 |
5 | This repo is intended to be used as a playground for a corresponding article:
6 |
7 | * Russian version: «[Мониторим основные сервисы в AWS с Prometheus и exporter’ами для CloudWatch](https://habr.com/ru/company/flant/blog/542082/)».
8 |
9 | ## Contents
10 |
11 | * `.helm` — Helm chart with Kubernetes manifests for cloudwatch_exporter & prometheus_aws_cost_exporter;
12 | it includes sample Prometheus rules for alerts (`.helm/templates/60-rules.yaml`);
13 | * `role.json` — AWS IAM permissions for prometheus_aws_cost_exporter;
14 | * `policy.json` & `terraform_user_policy.tf` — AWS IAM policy for API (in JSON) and Terraform.
15 |
--------------------------------------------------------------------------------
/2021/01-cloudwatch-exporter/policy.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Sid": "VisualEditor0",
6 | "Effect": "Allow",
7 | "Action": [
8 | "cloudwatch:GetMetricStatistics",
9 | "cloudwatch:ListMetrics"
10 | ],
11 | "Resource": "*"
12 | }
13 | ]
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/2021/01-cloudwatch-exporter/role.json:
--------------------------------------------------------------------------------
1 | {
2 | "Effect": "Allow",
3 | "Action": [
4 | "cloudwatch:PutMetricData",
5 | "ec2:DescribeVolumes",
6 | "ec2:DescribeTags",
7 | "logs:PutLogEvents",
8 | "logs:DescribeLogStreams",
9 | "logs:DescribeLogGroups",
10 | "logs:CreateLogStream",
11 | "logs:CreateLogGroup",
12 | "ce:GetCostAndUsage"
13 | ],
14 | "Resource": "*"
15 | },
16 | {
17 | "Effect": "Allow",
18 | "Action": [
19 | "ssm:GetParameter"
20 | ],
21 | "Resource": [
22 | "arn:aws:ssm:*:*:parameter/AmazonCloudWatch-*",
23 | "arn:aws:ce:*:*:/GetCostAndUsage"
24 | ]
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/2021/01-cloudwatch-exporter/werf.yaml:
--------------------------------------------------------------------------------
1 | project: cloudwatch-exporter
2 | configVersion: 1
3 | ---
4 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | appVersion: 12.0.4
3 | description: Open Source Identity and Access Management For Modern Applications and Services
4 | home: https://www.keycloak.org/
5 | icon: https://www.keycloak.org/resources/images/keycloak_logo_480x108.png
6 | keywords:
7 | - sso
8 | - idm
9 | - openid connect
10 | - saml
11 | - kerberos
12 | - ldap
13 | maintainers:
14 | - email: unguiculus@gmail.com
15 | name: unguiculus
16 | - email: thomas.darimont+github@gmail.com
17 | name: thomasdarimont
18 | name: keycloak
19 | sources:
20 | - https://github.com/codecentric/helm-charts
21 | - https://github.com/jboss-dockerfiles/keycloak
22 | - https://github.com/bitnami/charts/tree/master/bitnami/postgresql
23 | version: 10.0.0
24 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/scripts/keycloak.cli:
--------------------------------------------------------------------------------
1 | embed-server --server-config=standalone-ha.xml --std-out=echo
2 | batch
3 |
4 | echo Configuring node identifier
5 |
6 | ## Sets the node identifier to the node name (= pod name). Node identifiers have to be unique. They can have a
7 | ## maximum length of 23 characters. Thus, the chart's fullname template truncates its length accordingly.
8 | /subsystem=transactions:write-attribute(name=node-identifier, value=${jboss.node.name})
9 |
10 | echo Finished configuring node identifier
11 |
12 | run-batch
13 | stop-embedded-server
14 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/templates/_nodeselector.tpl:
--------------------------------------------------------------------------------
1 | {{ define "nodeselector" }}
2 | {{ if eq .Values.global.env "production" }}
3 | nodeSelector:
4 | node-role/production: ""
5 | {{ else }}
6 | {{ end }}
7 | {{ end }}
8 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/templates/_tolerations.tpl:
--------------------------------------------------------------------------------
1 | {{ define "toleration" }}
2 | {{ if eq .Values.global.env "production" }}
3 | tolerations:
4 | - key: "dedicated"
5 | operator: "Equal"
6 | value: "production"
7 | effect: "NoExecute"
8 | {{ else }}
9 | {{ end }}
10 | {{ end }}
11 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/templates/clusterrole.yaml:
--------------------------------------------------------------------------------
1 | kind: ClusterRole
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | metadata:
4 | name: keycloak-{{ .Values.global.env }}
5 | namespace: {{ .Release.Namespace | quote }}
6 | rules:
7 | - apiGroups: ["certificates.k8s.io"]
8 | resources: ["certificatesigningrequests"]
9 | verbs: ["create", "get", "watch"]
10 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/templates/clusterrolebinding.yaml:
--------------------------------------------------------------------------------
1 | kind: ClusterRoleBinding
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | metadata:
4 | name: keycloak-{{ .Values.global.env }}
5 | namespace: {{ .Release.Namespace | quote }}
6 | roleRef:
7 | apiGroup: rbac.authorization.k8s.io
8 | kind: ClusterRole
9 | name: keycloak-{{ .Values.global.env }}
10 | subjects:
11 | - kind: ServiceAccount
12 | name: keycloak-{{ .Values.global.env }}
13 | namespace: {{ .Release.Namespace | quote }}
14 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/templates/configmap-startup.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.startupScripts }}
2 | {{- $highAvailability := gt (int .Values.replicas) 1 -}}
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: {{ include "keycloak.fullname" . }}-startup
7 | labels:
8 | {{- include "keycloak.labels" . | nindent 4 }}
9 | data:
10 | {{- range $key, $value := .Values.startupScripts }}
11 | {{ $key }}: |
12 | {{- tpl $value $ | nindent 4 }}
13 | {{- end }}
14 | {{- end -}}
15 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/templates/poddisruptionbudget.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.podDisruptionBudget -}}
2 | apiVersion: policy/v1beta1
3 | kind: PodDisruptionBudget
4 | metadata:
5 | name: {{ include "keycloak.fullname" . }}
6 | labels:
7 | {{- include "keycloak.labels" . | nindent 4 }}
8 | spec:
9 | selector:
10 | matchLabels:
11 | {{- include "keycloak.selectorLabels" . | nindent 6 }}
12 | {{- toYaml .Values.podDisruptionBudget | nindent 2 }}
13 | {{- end -}}
14 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/templates/rbac.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.rbac.create .Values.rbac.rules }}
2 | kind: ClusterRole
3 | apiVersion: rbac.authorization.k8s.io/v1
4 | metadata:
5 | name: {{ include "keycloak.fullname" . }}
6 | rules:
7 | {{- toYaml .Values.rbac.rules | nindent 2 }}
8 | ---
9 | apiVersion: rbac.authorization.k8s.io/v1
10 | kind: ClusterRoleBinding
11 | metadata:
12 | name: {{ include "keycloak.fullname" . }}
13 | roleRef:
14 | apiGroup: rbac.authorization.k8s.io
15 | kind: ClusterRole
16 | name: {{ include "keycloak.fullname" . }}
17 | subjects:
18 | - kind: ServiceAccount
19 | name: {{ include "keycloak.serviceAccountName" . }}
20 | namespace: {{ .Release.Namespace }}
21 | {{- end }}
22 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/templates/secrets.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | data:
4 | ca.crt: {{ pluck .Values.global.env .Values.ca.crt | first | default .Values.ca.crt._default | b64enc }}
5 | tls.crt: {{ pluck .Values.global.env .Values.client.keycloak.crt | first | default .Values.client.keycloak.crt._default | b64enc }}
6 | tls.key: {{ pluck .Values.global.env .Values.client.keycloak.key | first | default .Values.client.keycloak.key._default | b64enc }}
7 | kind: Secret
8 | metadata:
9 | name: cockroachdb-keycloak
10 | type: kubernetes.io/tls
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/templates/service-headless.yaml:
--------------------------------------------------------------------------------
1 | {{- $highAvailability := gt (int .Values.replicas) 1 -}}
2 | apiVersion: v1
3 | kind: Service
4 | metadata:
5 | name: {{ include "keycloak.fullname" . }}-headless
6 | labels:
7 | {{- include "keycloak.labels" . | nindent 4 }}
8 | app.kubernetes.io/component: headless
9 | spec:
10 | type: ClusterIP
11 | clusterIP: None
12 | ports:
13 | - name: http
14 | port: {{ .Values.service.httpPort }}
15 | targetPort: http
16 | protocol: TCP
17 | {{- if $highAvailability }}
18 | - name: jgroups
19 | port: {{ .Values.service.jgroupsPort }}
20 | targetPort: jgroups
21 | protocol: TCP
22 | {{- end }}
23 | selector:
24 | {{- include "keycloak.selectorLabels" . | nindent 4 }}
25 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/templates/serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.serviceAccount.create -}}
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: keycloak-{{ .Values.global.env }}
6 | {{- with .Values.serviceAccount.annotations }}
7 | annotations:
8 | {{- range $key, $value := . }}
9 | {{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 4 }}
10 | {{- end }}
11 | {{- end }}
12 | labels:
13 | {{- include "keycloak.labels" . | nindent 4 }}
14 | {{- range $key, $value := .Values.serviceAccount.labels }}
15 | {{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 4 }}
16 | {{- end }}
17 | imagePullSecrets:
18 | {{- toYaml .Values.serviceAccount.imagePullSecrets | nindent 4 }}
19 | {{- end }}
20 |
--------------------------------------------------------------------------------
/2021/07-keycloak/.helm/templates/servicemonitor.yaml:
--------------------------------------------------------------------------------
1 | {{- with .Values.serviceMonitor -}}
2 | {{- if .enabled }}
3 | apiVersion: monitoring.coreos.com/v1
4 | kind: ServiceMonitor
5 | metadata:
6 | name: {{ include "keycloak.fullname" $ }}
7 | {{- with .namespace }}
8 | namespace: {{ . }}
9 | {{- end }}
10 | {{- with .annotations }}
11 | annotations:
12 | {{- range $key, $value := . }}
13 | {{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 4 }}
14 | {{- end }}
15 | {{- end }}
16 | labels:
17 | {{- include "keycloak.labels" $ | nindent 4 }}
18 | {{- range $key, $value := .labels }}
19 | {{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 4 }}
20 | {{- end }}
21 | spec:
22 | selector:
23 | matchLabels:
24 | {{- include "keycloak.selectorLabels" $ | nindent 6 }}
25 | app.kubernetes.io/component: http
26 | endpoints:
27 | - port: {{ .port }}
28 | path: {{ .path }}
29 | interval: {{ .interval }}
30 | scrapeTimeout: {{ .scrapeTimeout }}
31 | {{- end }}
32 | {{- end -}}
33 |
--------------------------------------------------------------------------------
/2021/07-keycloak/README.md:
--------------------------------------------------------------------------------
1 | # Deploying Keycloak with Infinispan in Kubernetes
2 |
3 | Here are sample configurations that can be used to deploy Keycloak in Kubernetes clusters.
4 |
5 | This repo is intended to be used as a playground for a corresponding article:
6 |
7 | * «[Running fault-tolerant Keycloak with Infinispan in Kubernetes](https://blog.flant.com/ha-keycloak-infinispan-kubernetes/)».
8 | * Russian version: «[Настраиваем отказоустойчивый Keycloak с Infinispan в Kubernetes](https://habr.com/ru/company/flant/blog/567626/)».
9 |
10 | ## Contents
11 |
12 | * `.helm` — a Helm chart with Kubernetes manifests for Keycloak (`templates/keycloak-sts.yaml`
13 | is for its StatefulSet, `templates/keycloak-cm.yaml` is for its ConfigMap) and Infinispan
14 | (`templates/infinispan-sts.yaml`, `templates/inifinispan-cm.yaml`);
15 | * `werf.yaml` — a simple [werf](https://werf.io/) configuration to build and deploy Keycloak
16 | with Infinispan that will have a newer version of the PostgreSQL driver
17 | (required for CockroachDB);
18 | * `jar` — JAR files used in `werf.yaml`.
19 |
--------------------------------------------------------------------------------
/2021/07-keycloak/jar/keycloak-metrics-spi-2.2.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2021/07-keycloak/jar/keycloak-metrics-spi-2.2.0.jar
--------------------------------------------------------------------------------
/2021/07-keycloak/jar/keycloak-model-jpa-12.0.4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2021/07-keycloak/jar/keycloak-model-jpa-12.0.4.jar
--------------------------------------------------------------------------------
/2021/07-keycloak/jar/postgresql-42.2.19.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2021/07-keycloak/jar/postgresql-42.2.19.jar
--------------------------------------------------------------------------------
/2021/09-gitea-gitlab-migration/README.md:
--------------------------------------------------------------------------------
1 | Here are Python scripts that can be used when migrating from Gitea to GitLab.
2 |
3 | This repo is intended to be used as a playground for a corresponding article:
4 |
5 | * «[Our experience with migrating from Gitea to GitLab. Challenging but successful](https://blog.flant.com/gitea-to-gitlab-migration/)».
6 | * Russian version: «[Опыт миграции из Gitea в GitLab. Сложно, но успешно](https://habr.com/ru/company/flant/blog/577808/)».
7 |
--------------------------------------------------------------------------------
/2021/09-gitea-gitlab-migration/requirements.txt:
--------------------------------------------------------------------------------
1 | giteapy
2 | python-gitlab
3 | requests
4 | bs4
5 | selenium
6 |
--------------------------------------------------------------------------------
/2021/09-memcached-mcrouter/README.md:
--------------------------------------------------------------------------------
1 | Here are sample configurations that can be used to deploy a highly available (HA) memcached in Kubernetes using mcrouter.
2 |
3 | This repo is intended to be used as a playground for a corresponding article:
4 |
5 | * «[Using mcrouter to make memcached highly available in Kubernetes](https://blog.flant.com/highly-available-memcached-with-mcrouter-in-kubernetes/)».
6 | * Russian version: «[Готовим высокодоступный memcached с mcrouter в Kubernetes](https://habr.com/ru/company/flant/blog/575656/)».
7 |
8 | ## Contents
9 |
10 | * `.helm` — a Helm chart with Kubernetes manifests for memcached & mcrouter (`templates/memcached-ds.yaml`
11 | is for memcached DaemonSet, `templates/mcrouter-ds.yaml` is for mcrouter DaemonSet, `templates/mcrouter-cm.yaml` is for mcrouter ConfigMap);
12 | * `werf.yaml` — a simple [werf](https://werf.io/) configuration to build and deploy memcached & mcrouter.
13 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.dockerignore:
--------------------------------------------------------------------------------
1 | /.helm/
2 |
3 | /log/*
4 | /tmp/*
5 |
6 | node_modules
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/Chart.lock:
--------------------------------------------------------------------------------
1 | dependencies:
2 | - name: app
3 | repository: ""
4 | version: "0.1"
5 | - name: mysql
6 | repository: ""
7 | version: "0.1"
8 | digest: sha256:fe182e918fab305c6f443eef420e835e9db110635fabb6601495671a598a2cb1
9 | generated: "2021-12-02T19:54:40.155157198+03:00"
10 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: werf-guide-app
3 | version: 1.0.0
4 | dependencies:
5 | - name: app
6 | version: 0.1
7 | condition: app.enabled
8 | export-values:
9 | - parent: werf
10 | child: werf
11 | - name: mysql
12 | version: 0.1
13 | condition: mysql.enabled
14 | export-values:
15 | - parent: werf
16 | child: werf
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/charts/app/Chart.yaml:
--------------------------------------------------------------------------------
1 | name: app
2 | version: 0.1
3 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/charts/app/templates/_envs_app.tpl:
--------------------------------------------------------------------------------
1 | {{- define "mysql_app_envs" }}
2 | - name: MYSQL_HOST
3 | value: "{{ pluck .Values.global.env .Values.envs.MYSQL_HOST | first | default .Values.envs.MYSQL_HOST._default }}"
4 | - name: MYSQL_DATABASE
5 | value: "{{ pluck .Values.global.env .Values.envs.MYSQL_DATABASE | first | default .Values.envs.MYSQL_DATABASE._default }}"
6 | - name: MYSQL_USERNAME
7 | value: "{{ pluck .Values.global.env .Values.envs.MYSQL_USERNAME | first | default .Values.envs.MYSQL_USERNAME._default }}"
8 | - name: MYSQL_PASSWORD
9 | valueFrom:
10 | secretKeyRef:
11 | name: {{ .Chart.Name }}
12 | key: MYSQL_PASSWORD
13 | {{- end }}
14 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/charts/app/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: networking.k8s.io/v1
3 | kind: Ingress
4 | metadata:
5 | annotations:
6 | kubernetes.io/ingress.class: nginx
7 | name: {{ .Chart.Name }}
8 | spec:
9 | rules:
10 | - host: {{ .Values.ci_url }}
11 | http:
12 | paths:
13 | - path: /
14 | pathType: Prefix
15 | backend:
16 | service:
17 | name: {{ .Chart.Name }}
18 | port:
19 | number: 80
20 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/charts/app/templates/secret.yaml:
--------------------------------------------------------------------------------
1 | {{ $password := (pluck .Values.global.env .Values.envs.MYSQL_PASSWORD | first | default .Values.envs.MYSQL_PASSWORD._default) }}
2 | ---
3 | apiVersion: v1
4 | kind: Secret
5 | type: Opaque
6 | metadata:
7 | name: {{ .Chart.Name }}
8 | data:
9 | MYSQL_PASSWORD: {{ $password | b64enc | quote }}
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/charts/app/templates/service.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Service
4 | metadata:
5 | name: {{ .Chart.Name }}
6 | spec:
7 | selector:
8 | app: {{ .Chart.Name }}
9 | ports:
10 | - name: http
11 | port: 80
12 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/charts/mysql/Chart.yaml:
--------------------------------------------------------------------------------
1 | name: mysql
2 | version: 0.1
3 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/charts/mysql/templates/_envs_database.tpl:
--------------------------------------------------------------------------------
1 | {{- define "mysql_database_envs" }}
2 | - name: MYSQL_DATABASE
3 | value: "{{ pluck .Values.global.env .Values.envs.MYSQL_DATABASE | first | default .Values.envs.MYSQL_DATABASE._default }}"
4 | - name: MYSQL_ROOT_PASSWORD
5 | valueFrom:
6 | secretKeyRef:
7 | name: {{ .Chart.Name }}
8 | key: MYSQL_ROOT_PASSWORD
9 | {{- end }}
10 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/charts/mysql/templates/mysql.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: apps/v1
3 | kind: StatefulSet
4 | metadata:
5 | name: {{ .Chart.Name }}
6 | spec:
7 | serviceName: {{ .Chart.Name }}
8 | selector:
9 | matchLabels:
10 | app: {{ .Chart.Name }}
11 | template:
12 | metadata:
13 | labels:
14 | app: {{ .Chart.Name }}
15 | spec:
16 | containers:
17 | - name: mysql
18 | image: {{ index .Values.werf.image "mysql" }}
19 | ports:
20 | - containerPort: 3306
21 | env: {{ include "mysql_database_envs" . | nindent 8 }}
22 | volumeMounts:
23 | - name: {{ .Chart.Name }}-data
24 | mountPath: /var/lib/mysql
25 | volumeClaimTemplates:
26 | - metadata:
27 | name: {{ .Chart.Name }}-data
28 | spec:
29 | accessModes: ["ReadWriteOnce"]
30 | resources:
31 | requests:
32 | storage: 100Mi
33 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/charts/mysql/templates/secret.yaml:
--------------------------------------------------------------------------------
1 | {{ $password := (pluck .Values.global.env .Values.envs.MYSQL_ROOT_PASSWORD | first | default .Values.envs.MYSQL_ROOT_PASSWORD._default) }}
2 | ---
3 | apiVersion: v1
4 | kind: Secret
5 | type: Opaque
6 | metadata:
7 | name: {{ .Chart.Name }}
8 | data:
9 | MYSQL_ROOT_PASSWORD: {{ $password | b64enc | quote }}
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/charts/mysql/templates/service.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Service
4 | metadata:
5 | name: {{ .Chart.Name }}
6 | spec:
7 | selector:
8 | app: {{ .Chart.Name }}
9 | ports:
10 | - port: 3306
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/secret-values.yaml:
--------------------------------------------------------------------------------
1 | app:
2 | envs:
3 | MYSQL_PASSWORD:
4 | _default: 100037f97cb2629e2cab648cabee0b33d2fe381d83a522e1ff2e5596614d50d3a055
5 | production: 1000b278b1a888b2f03aba0d21ad328007ab7a63199cb0f4d1d939250a3f6ab9d77d
6 | mysql:
7 | envs:
8 | MYSQL_ROOT_PASSWORD:
9 | _default: 100054065548f3910fcaa4f840f619722ae06b393bc7a3f2e496405b546163ce120e
10 | production: 100095009a7be85f03a73f4aabf6d4a3b9442444fbaa684faba8f507396955f34081
11 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.helm/values.yaml:
--------------------------------------------------------------------------------
1 | app:
2 | enabled: false
3 | envs:
4 | MYSQL_HOST:
5 | _default: mysql
6 | MYSQL_DATABASE:
7 | _default: werf
8 | local: werf-guide-app
9 | MYSQL_USERNAME:
10 | _default: werf
11 | local: root
12 | MYSQL_PASSWORD:
13 | local: Qwerty123
14 |
15 | mysql:
16 | enabled: false
17 | envs:
18 | MYSQL_ROOT_PASSWORD:
19 | local: Qwerty123
20 | MYSQL_DATABASE:
21 | local: werf-guide-app
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | singleQuote: true
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/.sequelizerc:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | config: path.resolve('config', 'database.js'),
5 | 'models-path': path.resolve('db', 'models'),
6 | 'seeders-path': path.resolve('db', 'seeders'),
7 | 'migrations-path': path.resolve('db', 'migrations'),
8 | };
9 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/README.md:
--------------------------------------------------------------------------------
1 | A simple application to demonstrate [local development in Kubernetes with werf 1.2 and minikube](https://blog.flant.com/local-development-in-kubernetes-with-werf/).
2 |
3 | > NB. Tested with Ubuntu 20.04 only.
4 |
5 | To start, follow these steps:
6 |
7 | ```
8 | cd local
9 | ./install.sh
10 | ./setup-infra.sh
11 | ./deploy-app.sh
12 | kubectl -n local get po
13 | ```
14 |
15 | Then visit http://test.application.local/.
16 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/config/database.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | module.exports = {
3 | "development": {
4 | "username": process.env.MYSQL_USERNAME,
5 | "password": process.env.MYSQL_PASSWORD,
6 | "database": process.env.MYSQL_DATABASE,
7 | "host": process.env.MYSQL_HOST,
8 | "dialect": "mysql"
9 | },
10 | "test": {
11 | "username": process.env.MYSQL_USERNAME,
12 | "password": process.env.MYSQL_PASSWORD,
13 | "database": process.env.MYSQL_DATABASE,
14 | "host": process.env.MYSQL_HOST,
15 | "dialect": "mysql"
16 | },
17 | "production": {
18 | "username": process.env.MYSQL_USERNAME,
19 | "password": process.env.MYSQL_PASSWORD,
20 | "database": process.env.MYSQL_DATABASE,
21 | "host": process.env.MYSQL_HOST,
22 | "dialect": "mysql"
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/db/migrations/20211101064002-create-talker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: async (queryInterface, Sequelize) => {
4 | await queryInterface.createTable('Talkers', {
5 | id: {
6 | allowNull: false,
7 | autoIncrement: true,
8 | primaryKey: true,
9 | type: Sequelize.INTEGER,
10 | },
11 | answer: {
12 | type: Sequelize.STRING,
13 | },
14 | name: {
15 | type: Sequelize.STRING,
16 | },
17 | createdAt: {
18 | allowNull: false,
19 | type: Sequelize.DATE,
20 | },
21 | updatedAt: {
22 | allowNull: false,
23 | type: Sequelize.DATE,
24 | },
25 | });
26 | },
27 | down: async (queryInterface, Sequelize) => {
28 | await queryInterface.dropTable('Talkers');
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/db/models/talker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const { Model } = require('sequelize');
3 | module.exports = (sequelize, DataTypes) => {
4 | class Talker extends Model {
5 | /**
6 | * Helper method for defining associations.
7 | * This method is not a part of Sequelize lifecycle.
8 | * The `models/index` file will call this method automatically.
9 | */
10 | static associate(models) {
11 | // define association here
12 | }
13 | }
14 | Talker.init(
15 | {
16 | answer: DataTypes.STRING,
17 | name: DataTypes.STRING,
18 | },
19 | {
20 | sequelize,
21 | modelName: 'Talker',
22 | }
23 | );
24 | return Talker;
25 | };
26 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/local/deploy-app.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # VARIABLES
4 | . variables
5 |
6 | RELEASE=application
7 |
8 | export WERF_INSECURE_REGISTRY=1
9 |
10 | cd ../
11 | if [[ $# -gt 0 ]]; then
12 | for argument in "$@"; do
13 | case $argument in
14 | -f|--follow)
15 | DEFAULT="--dev --follow"
16 | werf converge --repo $APP_REPO --release $RELEASE --env $ENV --namespace $NAMESPACE --set app.ci_url=$URL --set app.enabled=true $MODE --ignore-secret-key=true
17 | cd ./local
18 | ;;
19 | *)
20 | DEFAULT="--dev"
21 | werf converge --repo $APP_REPO --release $RELEASE --env $ENV --namespace $NAMESPACE --set app.ci_url=$URL --set app.enabled=true $MODE --ignore-secret-key=true
22 | cd ./local
23 | ;;
24 | esac
25 | done
26 | else
27 | werf converge --repo $APP_REPO --release $RELEASE --env $ENV --namespace $NAMESPACE --set app.ci_url=$URL --set app.enabled=true $MODE --ignore-secret-key=true
28 | fi
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/local/info/configure-docker.sh:
--------------------------------------------------------------------------------
1 | echo ""
2 | echo "####################################################################################"
3 | echo ""
4 | echo '
5 | Add new key to the file /etc/docker/daemon.json (default location). Create file if missing:
6 |
7 | {
8 | "insecure-registries": ["registry.local.dev:80"]
9 | }
10 |
11 | sudo systemctl restart docker
12 |
13 | '
14 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/local/info/install-kubectl.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | echo ""
3 | echo "#####################################################################################"
4 | echo ""
5 | echo "Installation manual: https://kubernetes.io/ru/docs/tasks/tools/install-kubectl/#%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-kubectl-%D0%B2-linux"
6 | echo ""
7 | echo "binary file can be installed like that: (copy+paste should be enough)"
8 | echo ""
9 | echo "curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl"
10 | echo "chmod +x ./kubectl"
11 | echo "sudo mv ./kubectl /usr/local/bin/kubectl"
12 | echo ""
13 | echo "Check everything is working fine:"
14 | echo "kubectl version --client"
15 | echo ""
16 | echo "#####################################################################################"
17 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/local/info/install-minikube.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | echo ""
3 | echo "#####################################################################################"
4 | echo ""
5 | echo "Installation manual: https://minikube.sigs.k8s.io/docs/start/"
6 | echo ""
7 | echo "binary file can be installed like that: (copy+paste should be enough)"
8 | echo ""
9 | echo "curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64"
10 | echo "sudo install minikube-linux-amd64 /usr/local/bin/minikube"
11 | echo ""
12 | echo "#####################################################################################"
13 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/local/info/install-werf.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | echo ""
3 | echo "#############################################################################################################"
4 | echo ""
5 | echo "Installation manual: https://werf.io/installation.html?version=1.2&channel=ea&os=linux&method=trdl&arch=amd64"
6 | echo '
7 | # Prepare user
8 | sudo groupadd docker
9 | sudo usermod -aG docker $USER
10 | newgrp docker
11 |
12 | # Add ~/bin to the PATH.
13 | echo "export PATH=$HOME/bin:$PATH" >> ~/.bash_profile
14 | export PATH="$HOME/bin:$PATH"
15 |
16 | # Install trdl.
17 | curl -L "https://tuf.trdl.dev/targets/releases/0.1.3/linux-amd64/bin/trdl" -o /tmp/trdl
18 | mkdir -p ~/bin
19 | install /tmp/trdl ~/bin/trdl
20 |
21 | # Add werf repo
22 | trdl add werf https://tuf.werf.io 1 b7ff6bcbe598e072a86d595a3621924c8612c7e6dc6a82e919abe89707d7e3f468e616b5635630680dd1e98fc362ae5051728406700e6274c5ed1ad92bea52a2
23 |
24 | # Enable werf
25 | source $(trdl use werf 1.2 ea)
26 | '
27 |
28 | printf "# Auto-enable werf on session start
29 | echo source \$(trdl use werf 1.2 ea) >> ~/.bashrc
30 |
31 | # Check evverything works
32 | werf version
33 | "
34 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/local/info/registry.sh:
--------------------------------------------------------------------------------
1 | echo ""
2 | echo "################################################################################################"
3 | echo ""
4 | echo '
5 | Set WERF_INSECURE_REGISTRY=1 environment variable in the terminal where werf would run. For bash:
6 |
7 | export WERF_INSECURE_REGISTRY=1
8 |
9 | To set this option automatically in new bash-sessions, add it to the .bashrc:
10 |
11 | echo export WERF_INSECURE_REGISTRY=1 | tee -a ~/.bashrc
12 |
13 | '
14 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/local/setup-infra.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # VARIABLES
4 | . variables
5 |
6 | RELEASE=infra
7 |
8 | export WERF_INSECURE_REGISTRY=1
9 |
10 | sudo echo
11 | ./prepare.sh
12 | while ! [[ "$(curl -w %{http_code} -sI http://$REPO/v2/ -o /dev/null)" -eq 200 ]] ; do echo "Waiting for registry..."; sleep 2; done;
13 | cd ..
14 | werf converge --repo $APP_REPO --release $RELEASE --env $ENV --namespace $NAMESPACE --dev --set mysql.enabled=true --ignore-secret-key=true
15 | cd ./local
16 |
17 | echo "$(minikube ip) $URL" | sudo tee -a /etc/hosts
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/local/variables:
--------------------------------------------------------------------------------
1 | ENV=local
2 | NAMESPACE=local
3 | URL=test.application.local
4 | REPO=registry.local.dev
5 | APP_REPO=$REPO/app
6 | MODE="--dev"
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/local/yaml/registry-ingress.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: networking.k8s.io/v1
3 | kind: Ingress
4 | metadata:
5 | name: registry
6 | namespace: kube-system
7 | annotations:
8 | nginx.ingress.kubernetes.io/proxy-body-size: "0"
9 | spec:
10 | rules:
11 | - host: hostname
12 | http:
13 | paths:
14 | - path: /
15 | pathType: Prefix
16 | backend:
17 | service:
18 | name: registry
19 | port:
20 | number: 80
21 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www",
7 | "build": "webpack"
8 | },
9 | "dependencies": {
10 | "cookie-parser": "~1.4.4",
11 | "debug": "~2.6.9",
12 | "express": "~4.18.2",
13 | "express-async-handler": "^1.2.0",
14 | "mysql2": "^2.3.3-rc.0",
15 | "pino": "^7.0.5",
16 | "pino-http": "^5.8.0",
17 | "dotenv": "^8.2.0",
18 | "sequelize": "^6.29.0",
19 | "sequelize-cli": "^6.3.0"
20 | },
21 | "devDependencies": {
22 | "css-loader": "^6.5.1",
23 | "css-minimizer-webpack-plugin": "^3.1.2",
24 | "html-webpack-plugin": "^5.5.0",
25 | "mini-css-extract-plugin": "^2.4.4",
26 | "webpack": "^5.76.0",
27 | "webpack-cli": "^4.9.1",
28 | "webpack-dev-middleware": "^5.2.1"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/public/assets/javascripts/image.js:
--------------------------------------------------------------------------------
1 | import Logo from '../images/werf-logo.svg';
2 | import '../stylesheets/image.css';
3 | import '../stylesheets/style.css';
4 |
5 | window.onload = function () {
6 | const btn = document.getElementById('show-image-btn');
7 | btn.addEventListener('click', (_) => {
8 | fetch(Logo)
9 | .then((data) => data.text())
10 | .then((html) => {
11 | const svgContainer = document.getElementById('container');
12 | svgContainer.insertAdjacentHTML('beforeend', html);
13 | const svg = svgContainer.getElementsByTagName('svg')[0];
14 | svg.setAttribute('id', 'image');
15 | btn.remove();
16 | });
17 | });
18 | };
19 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/public/assets/javascripts/index.js:
--------------------------------------------------------------------------------
1 | import '../stylesheets/style.css';
2 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/public/assets/stylesheets/image.css:
--------------------------------------------------------------------------------
1 | #container {
2 | height: 50vh;
3 | text-align: center;
4 | display: grid;
5 | }
6 |
7 | #image {
8 | margin: auto;
9 | height: auto;
10 | width: 30vh;
11 | }
12 |
13 | #show-image-btn {
14 | margin: auto;
15 | padding: 0.7em 1.5em;
16 | font-size: large;
17 | }
18 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/public/assets/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px 'Lucida Grande', Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00b7ff;
8 | }
9 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/public/pages/image.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Werf Logo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/public/pages/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Werf Guide App
4 |
5 |
6 |
7 | Wef Guide App based on Express
8 | Welcome to Werf guide. Visit the image page.
9 |
10 |
11 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/routes/image.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | /* Image page. */
5 | router.get('/', express.static('dist/image.html'));
6 |
7 | module.exports = router;
8 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/routes/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const router = express.Router();
4 |
5 | /* GET home page. */
6 | router.get('/', function (req, res, next) {
7 | res.sendFile(path.resolve(process.cwd(), 'dist', 'index.html'));
8 | });
9 |
10 | router.get('/err', function (req, res, next) {
11 | throw new Error('Hello from an unhandler error');
12 | });
13 |
14 | module.exports = router;
15 |
--------------------------------------------------------------------------------
/2022/01-werf-local-dev/routes/ping.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | router.get('/', function (req, res, next) {
5 | res.log.debug('we are being pinged');
6 | res.send('pong\n');
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/2022/06-okteto/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:buster
2 | COPY . /app
3 | RUN cd /app && \
4 | go build main.go && \
5 | chmod +x /app/main
6 | CMD /app/main
7 |
--------------------------------------------------------------------------------
/2022/06-okteto/README.md:
--------------------------------------------------------------------------------
1 | Here are samples you can use to try Okteto for your local development.
2 |
3 | This repo is intended to be used as a playground for a corresponding article:
4 |
5 | * «**[Okteto Cloud as another way for local development in Kubernetes](https://blog.flant.com/okteto-cloud-for-local-development-in-kubernetes/)**».
6 | * Russian version: «[Okteto Cloud – еще один удобный способ организации локальной разработки](https://habr.com/ru/company/flant/blog/667806/)».
7 |
8 | ## Contents
9 |
10 | * `main.go` — a hello-world app in Go;
11 | * `Dockerfile` — a Dockerfile required to build this app;
12 | * `okteto.yml` — a simple configuration for Okteto used to launch a dev environment with this app.
13 |
--------------------------------------------------------------------------------
/2022/06-okteto/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | )
7 |
8 | func main() {
9 | fmt.Println("Starting hello-world server...")
10 | http.HandleFunc("/", helloServer)
11 | if err := http.ListenAndServe(":8080", nil); err != nil {
12 | panic(err)
13 | }
14 | }
15 |
16 | func helloServer(w http.ResponseWriter, r *http.Request) {
17 | fmt.Fprint(w, "Hello from Okteto!")
18 | }
19 |
--------------------------------------------------------------------------------
/2022/06-okteto/okteto.yml:
--------------------------------------------------------------------------------
1 | name: okteto-test
2 | autocreate: true
3 | image: okteto/golang:1
4 | command: bash
5 | #namespace: okteto
6 | securityContext:
7 | capabilities:
8 | add:
9 | - SYS_PTRACE
10 | sync:
11 | - .:/usr/src/app
12 | forward:
13 | - 2345:2345
14 | - 8080:8080
15 | persistentVolume:
16 | enabled: true
17 |
--------------------------------------------------------------------------------
/2022/06-werf-kube-run/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.18-alpine
2 | WORKDIR /app
3 | ADD . /app/
4 | RUN go build -o main .
5 | RUN chmod +x ./main
6 | CMD ./main
--------------------------------------------------------------------------------
/2022/06-werf-kube-run/README.md:
--------------------------------------------------------------------------------
1 | A simple application written in Go to demonstrate how you can use [werf kube-run](https://werf.io/documentation/v1.2/reference/cli/werf_kube_run.html),
2 | a new [werf](https://github.com/werf/werf) command.
3 |
4 | You will need to have a Kubernetes environment, e.g. based on minikube or
5 | an access to any other K8s cluster where this app will run.
6 |
7 | This repo is intended to be used as a playground for a corresponding article:
8 |
9 | * «**[Running one-time tasks and debugging images in the Kubernetes cluster using werf](https://blog.flant.com/kubectl-run-tasks-in-kubernetes-with-werf/)**».
10 | * Russian version: «[Запуск одноразовых задач и отладка образов прямо в Kubernetes-кластере с помощью werf](https://habr.com/ru/company/flant/blog/671960/)».
11 |
--------------------------------------------------------------------------------
/2022/06-werf-kube-run/go.mod:
--------------------------------------------------------------------------------
1 | module square
2 |
3 | go 1.18
4 |
--------------------------------------------------------------------------------
/2022/06-werf-kube-run/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | )
7 |
8 | // A function for calculating the area of a rectangle.
9 | func getArea(x, y int) (res int) {
10 | return x * y
11 | }
12 |
13 | func main() {
14 | fmt.Println("Rectangle area: " + strconv.Itoa(getArea(10, 10)))
15 | }
16 |
--------------------------------------------------------------------------------
/2022/06-werf-kube-run/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "testing"
4 |
5 | func testGetArea(t *testing.T) {
6 |
7 | got := getArea(3, 2)
8 | want := 6
9 |
10 | if got != want {
11 | t.Errorf("Expected %q, received %q", got, want)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/2022/06-werf-kube-run/werf.yaml:
--------------------------------------------------------------------------------
1 | project: werf-kuberun-app
2 | configVersion: 1
3 |
4 | ---
5 | image: kuberun
6 | dockerfile: Dockerfile
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/.dockerignore:
--------------------------------------------------------------------------------
1 | /.helm/
2 |
3 | /log/*
4 | /tmp/*
5 |
6 | node_modules
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/.helm/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: werf-first-app
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: werf-first-app
10 | template:
11 | metadata:
12 | labels:
13 | app: werf-first-app
14 | spec:
15 | imagePullSecrets:
16 | - name: registrysecret
17 | containers:
18 | - name: app
19 | image: {{ .Values.werf.image.app }}
20 | command: ["npm", "start"]
21 | ports:
22 | - containerPort: 3000
23 | env:
24 | - name: DEBUG
25 | value: "app:*"
26 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/.helm/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | annotations:
5 | kubernetes.io/ingress.class: nginx
6 | name: werf-first-app
7 | spec:
8 | rules:
9 | - host: werf-first-app.test
10 | http:
11 | paths:
12 | - path: /
13 | pathType: Prefix
14 | backend:
15 | service:
16 | name: werf-first-app
17 | port:
18 | number: 3000
19 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/.helm/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: werf-first-app
5 | spec:
6 | selector:
7 | app: werf-first-app
8 | ports:
9 | - name: http
10 | port: 3000
11 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | singleQuote: true
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:12-alpine
2 | WORKDIR /app
3 |
4 | # [] Copy the files needed to install the application dependencies into the image.
5 | # [] Копируем в образ файлы, нужные для установки зависимостей приложения.
6 | COPY package.json package-lock.json ./
7 | # [] Install the application dependencies.
8 | # [] Устанавливаем зависимости приложения.
9 | RUN npm ci
10 |
11 | # [] Copy all other application files into the image.
12 | # [] Копируем в образ все остальные файлы приложения.
13 | COPY . .
14 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/app.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const cookieParser = require('cookie-parser');
4 | const logger = require('morgan');
5 |
6 | // []
7 | const indexRouter = require('./routes/index');
8 | const pingRouter = require('./routes/ping');
9 | // []
10 |
11 | const app = express();
12 |
13 | app.use(logger('dev'));
14 | app.use(express.json());
15 | app.use(express.urlencoded({ extended: false }));
16 | app.use(cookieParser());
17 | app.use(express.static(path.join(__dirname, 'public')));
18 |
19 | // []
20 | app.use('/', indexRouter);
21 | app.use('/ping', pingRouter);
22 | // []
23 |
24 | module.exports = app;
25 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www"
7 | },
8 | "dependencies": {
9 | "cookie-parser": "~1.4.4",
10 | "debug": "~2.6.9",
11 | "express": "~4.18.2",
12 | "morgan": "~1.9.1"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Express
4 |
5 |
6 |
7 |
8 | Express
9 | Welcome to Express
10 |
11 |
12 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px 'Lucida Grande', Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00b7ff;
8 | }
9 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/routes/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | /* GET home page. */
5 | router.get('/', function (req, res, next) {
6 | res.render('index', { title: 'Express' });
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/routes/ping.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | router.get('/', function (req, res, next) {
5 | res.send('Hello, werfer!\n');
6 | });
7 |
8 | module.exports = router;
9 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/01_basic_app/werf.yaml:
--------------------------------------------------------------------------------
1 | project: werf-first-app
2 | configVersion: 1
3 |
4 | ---
5 | image: app
6 | dockerfile: Dockerfile
7 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/.dockerignore:
--------------------------------------------------------------------------------
1 | /.helm/
2 |
3 | /log/*
4 | /tmp/*
5 |
6 | node_modules
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/.helm/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: werf-first-app
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: werf-first-app
10 | template:
11 | metadata:
12 | labels:
13 | app: werf-first-app
14 | spec:
15 | imagePullSecrets:
16 | - name: registrysecret
17 | containers:
18 | - name: backend
19 | image: {{ .Values.werf.image.backend }}
20 | command: ["node", "./bin/www"]
21 | ports:
22 | - containerPort: 3000
23 | env:
24 | - name: NODE_ENV
25 | value: production
26 | - name: frontend
27 | image: {{ .Values.werf.image.frontend }}
28 | ports:
29 | - containerPort: 80
30 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/.helm/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | annotations:
5 | kubernetes.io/ingress.class: nginx
6 | name: werf-first-app
7 | spec:
8 | rules:
9 | - host: werf-first-app.test
10 | http:
11 | paths:
12 | - path: /
13 | pathType: Prefix
14 | backend:
15 | service:
16 | name: werf-first-app
17 | port:
18 | number: 80
19 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/.helm/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: werf-first-app
5 | spec:
6 | selector:
7 | app: werf-first-app
8 | ports:
9 | - name: http
10 | port: 80
11 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | singleQuote: true
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www",
7 | "build": "webpack"
8 | },
9 | "dependencies": {
10 | "cookie-parser": "~1.4.4",
11 | "debug": "~2.6.9",
12 | "express": "~4.18.2",
13 | "pino": "^7.0.5",
14 | "pino-http": "^5.8.0"
15 | },
16 | "devDependencies": {
17 | "css-loader": "^6.5.1",
18 | "css-minimizer-webpack-plugin": "^3.1.2",
19 | "html-webpack-plugin": "^5.5.0",
20 | "mini-css-extract-plugin": "^2.4.4",
21 | "webpack": "^5.76.0",
22 | "webpack-cli": "^4.9.1",
23 | "webpack-dev-middleware": "^5.2.1"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/public/assets/javascripts/image.js:
--------------------------------------------------------------------------------
1 | import Logo from '../images/werf-logo.svg';
2 | import '../stylesheets/image.css';
3 | import '../stylesheets/style.css';
4 |
5 | window.onload = function () {
6 | const btn = document.getElementById('show-image-btn');
7 | btn.addEventListener('click', (_) => {
8 | fetch(Logo)
9 | .then((data) => data.text())
10 | .then((html) => {
11 | const svgContainer = document.getElementById('container');
12 | svgContainer.insertAdjacentHTML('beforeend', html);
13 | const svg = svgContainer.getElementsByTagName('svg')[0];
14 | svg.setAttribute('id', 'image');
15 | btn.remove();
16 | });
17 | });
18 | };
19 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/public/assets/javascripts/index.js:
--------------------------------------------------------------------------------
1 | import '../stylesheets/style.css';
2 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/public/assets/stylesheets/image.css:
--------------------------------------------------------------------------------
1 | #container {
2 | height: 50vh;
3 | text-align: center;
4 | display: grid;
5 | }
6 |
7 | #image {
8 | margin: auto;
9 | height: auto;
10 | width: 30vh;
11 | }
12 |
13 | #show-image-btn {
14 | margin: auto;
15 | padding: 0.7em 1.5em;
16 | font-size: large;
17 | }
18 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/public/assets/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px 'Lucida Grande', Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00b7ff;
8 | }
9 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/public/pages/image.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | werf logo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/public/pages/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | werf first app
4 |
5 |
6 |
7 | werf first app based on Express
8 | Welcome to werf guide. Visit the image page.
9 |
10 |
11 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/routes/image.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | /* Image page. */
5 | router.get('/', express.static('dist/image.html'));
6 |
7 | module.exports = router;
8 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/routes/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const router = express.Router();
4 |
5 | // []
6 | /* GET home page. */
7 | router.get('/', function (req, res, next) {
8 | res.sendFile(path.resolve(process.cwd(), 'dist', 'index.html'));
9 | });
10 | // []
11 |
12 | router.get('/err', function (req, res, next) {
13 | throw new Error('Hello from an unhandler error');
14 | });
15 |
16 | module.exports = router;
17 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/routes/ping.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | router.get('/', function (req, res, next) {
5 | res.log.debug('we are being pinged');
6 | res.send('Hello, werfer!\n');
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/020_assets/werf.yaml:
--------------------------------------------------------------------------------
1 | project: werf-first-app
2 | configVersion: 1
3 |
4 | ---
5 | image: backend
6 | dockerfile: Dockerfile
7 | target: backend
8 |
9 | ---
10 | image: frontend
11 | dockerfile: Dockerfile
12 | target: frontend
13 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/.dockerignore:
--------------------------------------------------------------------------------
1 | /.helm/
2 |
3 | /log/*
4 | /tmp/*
5 |
6 | node_modules
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/.helm/templates/database.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: mysql
5 | spec:
6 | serviceName: mysql
7 | selector:
8 | matchLabels:
9 | app: mysql
10 | template:
11 | metadata:
12 | labels:
13 | app: mysql
14 | spec:
15 | containers:
16 | - name: mysql
17 | image: mysql:5.7
18 | ports:
19 | - containerPort: 3306
20 | env:
21 | - name: MYSQL_ROOT_PASSWORD
22 | value: password
23 | volumeMounts:
24 | - name: mysql-data
25 | mountPath: /var/lib/mysql
26 | volumeClaimTemplates:
27 | - metadata:
28 | name: mysql-data
29 | spec:
30 | accessModes: ["ReadWriteOnce"]
31 | resources:
32 | requests:
33 | storage: 100Mi
34 |
35 | ---
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: mysql
40 | spec:
41 | selector:
42 | app: mysql
43 | ports:
44 | - port: 3306
45 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/.helm/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | annotations:
5 | kubernetes.io/ingress.class: nginx
6 | name: werf-first-app
7 | spec:
8 | rules:
9 | - host: werf-first-app.test
10 | http:
11 | paths:
12 | - path: /
13 | pathType: Prefix
14 | backend:
15 | service:
16 | name: werf-first-app
17 | port:
18 | number: 80
19 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/.helm/templates/job-db-setup-and-migrate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | name: "setup-and-migrate-db-rev{{ .Release.Revision }}"
5 | spec:
6 | backoffLimit: 0
7 | template:
8 | spec:
9 | restartPolicy: Never
10 | imagePullSecrets:
11 | - name: registrysecret
12 | containers:
13 | - name: setup-and-migrate-db
14 | image: {{ .Values.werf.image.backend }}
15 | command:
16 | - sh
17 | - -euc
18 | - |
19 | is_mysql_available() {
20 | tries=$1
21 | i=0
22 | while [ $i -lt $tries ]; do
23 | mysqladmin -h mysql -P 3306 -u root -p=password ping || return 1
24 | i=$((i+1))
25 | sleep 1
26 | done
27 | }
28 |
29 | until is_mysql_available 10; do
30 | sleep 1
31 | done
32 |
33 | ./node_modules/.bin/sequelize-cli db:create
34 | ./node_modules/.bin/sequelize-cli db:migrate
35 | env:
36 | - name: NODE_ENV
37 | value: production
38 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/.helm/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: werf-first-app
5 | spec:
6 | selector:
7 | app: werf-first-app
8 | ports:
9 | - name: http
10 | port: 80
11 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | singleQuote: true
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/.sequelizerc:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | config: path.resolve('config', 'database.json'),
5 | 'models-path': path.resolve('db', 'models'),
6 | 'seeders-path': path.resolve('db', 'seeders'),
7 | 'migrations-path': path.resolve('db', 'migrations'),
8 | };
9 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/config/database.json:
--------------------------------------------------------------------------------
1 | {
2 | "development": {
3 | "username": "root",
4 | "password": "null",
5 | "database": "werf-first-app",
6 | "host": "mysql",
7 | "dialect": "mysql"
8 | },
9 | "test": {
10 | "username": "root",
11 | "password": null,
12 | "database": "werf-first-app",
13 | "host": "mysql",
14 | "dialect": "mysql"
15 | },
16 | "production": {
17 | "username": "root",
18 | "password": "password",
19 | "database": "werf-first-app",
20 | "host": "mysql",
21 | "dialect": "mysql"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/db/migrations/20211101064002-create-talker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: async (queryInterface, Sequelize) => {
4 | await queryInterface.createTable('Talkers', {
5 | id: {
6 | allowNull: false,
7 | autoIncrement: true,
8 | primaryKey: true,
9 | type: Sequelize.INTEGER,
10 | },
11 | answer: {
12 | type: Sequelize.STRING,
13 | },
14 | name: {
15 | type: Sequelize.STRING,
16 | },
17 | createdAt: {
18 | allowNull: false,
19 | type: Sequelize.DATE,
20 | },
21 | updatedAt: {
22 | allowNull: false,
23 | type: Sequelize.DATE,
24 | },
25 | });
26 | },
27 | down: async (queryInterface, Sequelize) => {
28 | await queryInterface.dropTable('Talkers');
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/db/models/talker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const { Model } = require('sequelize');
3 | module.exports = (sequelize, DataTypes) => {
4 | class Talker extends Model {
5 | /**
6 | * Helper method for defining associations.
7 | * This method is not a part of Sequelize lifecycle.
8 | * The `models/index` file will call this method automatically.
9 | */
10 | static associate(models) {
11 | // Define association here.
12 | }
13 | }
14 | Talker.init(
15 | {
16 | answer: DataTypes.STRING,
17 | name: DataTypes.STRING,
18 | },
19 | {
20 | sequelize,
21 | modelName: 'Talker',
22 | }
23 | );
24 | return Talker;
25 | };
26 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www",
7 | "build": "webpack"
8 | },
9 | "dependencies": {
10 | "cookie-parser": "~1.4.4",
11 | "debug": "~2.6.9",
12 | "express": "~4.18.2",
13 | "express-async-handler": "^1.2.0",
14 | "mysql2": "^2.3.3-rc.0",
15 | "pino": "^7.0.5",
16 | "pino-http": "^5.8.0",
17 | "sequelize": "^6.29.0",
18 | "sequelize-cli": "^6.3.0"
19 | },
20 | "devDependencies": {
21 | "css-loader": "^6.5.1",
22 | "css-minimizer-webpack-plugin": "^3.1.2",
23 | "html-webpack-plugin": "^5.5.0",
24 | "mini-css-extract-plugin": "^2.4.4",
25 | "webpack": "^5.76.0",
26 | "webpack-cli": "^4.9.1",
27 | "webpack-dev-middleware": "^5.2.1"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/public/assets/javascripts/image.js:
--------------------------------------------------------------------------------
1 | import Logo from '../images/werf-logo.svg';
2 | import '../stylesheets/image.css';
3 | import '../stylesheets/style.css';
4 |
5 | window.onload = function () {
6 | const btn = document.getElementById('show-image-btn');
7 | btn.addEventListener('click', (_) => {
8 | fetch(Logo)
9 | .then((data) => data.text())
10 | .then((html) => {
11 | const svgContainer = document.getElementById('container');
12 | svgContainer.insertAdjacentHTML('beforeend', html);
13 | const svg = svgContainer.getElementsByTagName('svg')[0];
14 | svg.setAttribute('id', 'image');
15 | btn.remove();
16 | });
17 | });
18 | };
19 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/public/assets/javascripts/index.js:
--------------------------------------------------------------------------------
1 | import '../stylesheets/style.css';
2 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/public/assets/stylesheets/image.css:
--------------------------------------------------------------------------------
1 | #container {
2 | height: 50vh;
3 | text-align: center;
4 | display: grid;
5 | }
6 |
7 | #image {
8 | margin: auto;
9 | height: auto;
10 | width: 30vh;
11 | }
12 |
13 | #show-image-btn {
14 | margin: auto;
15 | padding: 0.7em 1.5em;
16 | font-size: large;
17 | }
18 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/public/assets/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px 'Lucida Grande', Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00b7ff;
8 | }
9 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/public/pages/image.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | werf logo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/public/pages/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | werf first app
4 |
5 |
6 |
7 | werf first app based on Express
8 | Welcome to werf guide. Visit the image page.
9 |
10 |
11 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/routes/image.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | /* Image page. */
5 | router.get('/', express.static('dist/image.html'));
6 |
7 | module.exports = router;
8 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/routes/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const router = express.Router();
4 |
5 | /* GET home page. */
6 | router.get('/', function (req, res, next) {
7 | res.sendFile(path.resolve(process.cwd(), 'dist', 'index.html'));
8 | });
9 |
10 | router.get('/err', function (req, res, next) {
11 | throw new Error('Hello from an unhandler error');
12 | });
13 |
14 | module.exports = router;
15 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/routes/ping.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | router.get('/', function (req, res, next) {
5 | res.log.debug('we are being pinged');
6 | res.send('Hello, werfer!\n');
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/030_db/werf.yaml:
--------------------------------------------------------------------------------
1 | project: werf-first-app
2 | configVersion: 1
3 |
4 | ---
5 | image: backend
6 | dockerfile: Dockerfile
7 | target: backend
8 |
9 | ---
10 | image: frontend
11 | dockerfile: Dockerfile
12 | target: frontend
13 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/.dockerignore:
--------------------------------------------------------------------------------
1 | /.helm/
2 |
3 | /log/*
4 | /tmp/*
5 |
6 | node_modules
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/.helm/templates/database.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: mysql
5 | spec:
6 | serviceName: mysql
7 | selector:
8 | matchLabels:
9 | app: mysql
10 | template:
11 | metadata:
12 | labels:
13 | app: mysql
14 | spec:
15 | containers:
16 | - name: mysql
17 | image: mysql:5.7
18 | ports:
19 | - containerPort: 3306
20 | env:
21 | - name: MYSQL_ROOT_PASSWORD
22 | value: password
23 | volumeMounts:
24 | - name: mysql-data
25 | mountPath: /var/lib/mysql
26 | volumeClaimTemplates:
27 | - metadata:
28 | name: mysql-data
29 | spec:
30 | accessModes: ["ReadWriteOnce"]
31 | resources:
32 | requests:
33 | storage: 100Mi
34 |
35 | ---
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: mysql
40 | spec:
41 | selector:
42 | app: mysql
43 | ports:
44 | - port: 3306
45 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/.helm/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | annotations:
5 | kubernetes.io/ingress.class: nginx
6 | name: werf-first-app
7 | spec:
8 | rules:
9 | - host: werf-first-app.test
10 | http:
11 | paths:
12 | - path: /
13 | pathType: Prefix
14 | backend:
15 | service:
16 | name: werf-first-app
17 | port:
18 | number: 80
19 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/.helm/templates/job-setup-minio.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | name: "setup-minio-rev{{ .Release.Revision }}"
5 | spec:
6 | backoffLimit: 0
7 | template:
8 | spec:
9 | restartPolicy: Never
10 | containers:
11 | - name: setup-minio
12 | image: minio/mc
13 | command:
14 | - sh
15 | - -euc
16 | - |
17 | is_minio_available() {
18 | tries=$1
19 | i=0
20 | while [ $i -lt $tries ]; do
21 | curl -sSL http://minio:9000/minio/health/live || return 1
22 | i=$((i+1))
23 | sleep 1
24 | done
25 | }
26 |
27 | until is_minio_available 10; do
28 | sleep 1
29 | done
30 |
31 | mc alias set minio http://minio:9000 minioadmin minioadmin
32 |
33 | mc mb --ignore-existing minio/werf-first-app
34 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/.helm/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: werf-first-app
5 | spec:
6 | selector:
7 | app: werf-first-app
8 | ports:
9 | - name: http
10 | port: 80
11 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/.helm/templates/storage.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: minio
5 | spec:
6 | serviceName: minio
7 | selector:
8 | matchLabels:
9 | app: minio
10 | template:
11 | metadata:
12 | labels:
13 | app: minio
14 | spec:
15 | containers:
16 | - name: minio
17 | image: minio/minio
18 | args: ["server", "/data", "--console-address", ":9001"]
19 | ports:
20 | - containerPort: 9000
21 | name: minio
22 | - containerPort: 9001
23 | name: console
24 | volumeMounts:
25 | - name: minio-data
26 | mountPath: /data
27 | volumeClaimTemplates:
28 | - metadata:
29 | name: minio-data
30 | spec:
31 | accessModes: ["ReadWriteOnce"]
32 | resources:
33 | requests:
34 | storage: 100Mi
35 |
36 | ---
37 | apiVersion: v1
38 | kind: Service
39 | metadata:
40 | name: minio
41 | spec:
42 | selector:
43 | app: minio
44 | ports:
45 | - port: 9000
46 | name: minio
47 | - port: 9001
48 | name: console
49 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | singleQuote: true
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/.sequelizerc:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | config: path.resolve('config', 'database.json'),
5 | 'models-path': path.resolve('db', 'models'),
6 | 'seeders-path': path.resolve('db', 'seeders'),
7 | 'migrations-path': path.resolve('db', 'migrations'),
8 | };
9 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/config/database.json:
--------------------------------------------------------------------------------
1 | {
2 | "development": {
3 | "username": "root",
4 | "password": "null",
5 | "database": "werf-first-app",
6 | "host": "mysql",
7 | "dialect": "mysql"
8 | },
9 | "test": {
10 | "username": "root",
11 | "password": null,
12 | "database": "werf-first-app",
13 | "host": "mysql",
14 | "dialect": "mysql"
15 | },
16 | "production": {
17 | "username": "root",
18 | "password": "password",
19 | "database": "werf-first-app",
20 | "host": "mysql",
21 | "dialect": "mysql"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/config/minio.json:
--------------------------------------------------------------------------------
1 | {
2 | "region": "us-east-1",
3 | "endpoint": {
4 | "protocol": "http",
5 | "hostname": "minio",
6 | "port": 9000,
7 | "path": "/"
8 | },
9 | "bucket": "werf-first-app",
10 | "forcePathStyle": true,
11 | "credentials": {
12 | "accessKeyId": "minioadmin",
13 | "secretAccessKey": "minioadmin"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/db/migrations/20211101064002-create-talker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | up: async (queryInterface, Sequelize) => {
4 | await queryInterface.createTable('Talkers', {
5 | id: {
6 | allowNull: false,
7 | autoIncrement: true,
8 | primaryKey: true,
9 | type: Sequelize.INTEGER,
10 | },
11 | answer: {
12 | type: Sequelize.STRING,
13 | },
14 | name: {
15 | type: Sequelize.STRING,
16 | },
17 | createdAt: {
18 | allowNull: false,
19 | type: Sequelize.DATE,
20 | },
21 | updatedAt: {
22 | allowNull: false,
23 | type: Sequelize.DATE,
24 | },
25 | });
26 | },
27 | down: async (queryInterface, Sequelize) => {
28 | await queryInterface.dropTable('Talkers');
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/db/models/talker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const { Model } = require('sequelize');
3 | module.exports = (sequelize, DataTypes) => {
4 | class Talker extends Model {
5 | /**
6 | * Helper method for defining associations.
7 | * This method is not a part of Sequelize lifecycle.
8 | * The `models/index` file will call this method automatically.
9 | */
10 | static associate(models) {
11 | // define association here
12 | }
13 | }
14 | Talker.init(
15 | {
16 | answer: DataTypes.STRING,
17 | name: DataTypes.STRING,
18 | },
19 | {
20 | sequelize,
21 | modelName: 'Talker',
22 | }
23 | );
24 | return Talker;
25 | };
26 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www",
7 | "build": "webpack"
8 | },
9 | "dependencies": {
10 | "@aws-sdk/client-s3": "^3.515.0",
11 | "cookie-parser": "~1.4.4",
12 | "debug": "~2.6.9",
13 | "express": "~4.18.2",
14 | "express-async-handler": "^1.2.0",
15 | "express-fileupload": "^1.2.1",
16 | "mysql2": "^2.3.3-rc.0",
17 | "pino": "^7.0.5",
18 | "pino-http": "^5.8.0",
19 | "readable-web-to-node-stream": "^3.0.2",
20 | "sequelize": "^6.29.0",
21 | "sequelize-cli": "^6.3.0"
22 | },
23 | "devDependencies": {
24 | "css-loader": "^6.5.1",
25 | "css-minimizer-webpack-plugin": "^3.1.2",
26 | "html-webpack-plugin": "^5.5.0",
27 | "mini-css-extract-plugin": "^2.4.4",
28 | "webpack": "^5.76.0",
29 | "webpack-cli": "^4.9.1",
30 | "webpack-dev-middleware": "^5.2.1"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/public/assets/javascripts/image.js:
--------------------------------------------------------------------------------
1 | import Logo from '../images/werf-logo.svg';
2 | import '../stylesheets/image.css';
3 | import '../stylesheets/style.css';
4 |
5 | window.onload = function () {
6 | const btn = document.getElementById('show-image-btn');
7 | btn.addEventListener('click', (_) => {
8 | fetch(Logo)
9 | .then((data) => data.text())
10 | .then((html) => {
11 | const svgContainer = document.getElementById('container');
12 | svgContainer.insertAdjacentHTML('beforeend', html);
13 | const svg = svgContainer.getElementsByTagName('svg')[0];
14 | svg.setAttribute('id', 'image');
15 | btn.remove();
16 | });
17 | });
18 | };
19 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/public/assets/javascripts/index.js:
--------------------------------------------------------------------------------
1 | import '../stylesheets/style.css';
2 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/public/assets/stylesheets/image.css:
--------------------------------------------------------------------------------
1 | #container {
2 | height: 50vh;
3 | text-align: center;
4 | display: grid;
5 | }
6 |
7 | #image {
8 | margin: auto;
9 | height: auto;
10 | width: 30vh;
11 | }
12 |
13 | #show-image-btn {
14 | margin: auto;
15 | padding: 0.7em 1.5em;
16 | font-size: large;
17 | }
18 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/public/assets/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px 'Lucida Grande', Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00b7ff;
8 | }
9 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/public/pages/image.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | werf logo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/public/pages/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | werf first app
4 |
5 |
6 |
7 | werf first app based on Express
8 | Welcome to werf guide. Visit the image page.
9 |
10 |
11 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/routes/image.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | /* Image page. */
5 | router.get('/', express.static('dist/image.html'));
6 |
7 | module.exports = router;
8 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/routes/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const path = require('path');
3 | const router = express.Router();
4 |
5 | /* GET home page. */
6 | router.get('/', function (req, res, next) {
7 | res.sendFile(path.resolve(process.cwd(), 'dist', 'index.html'));
8 | });
9 |
10 | router.get('/err', function (req, res, next) {
11 | throw new Error('Hello from an unhandler error');
12 | });
13 |
14 | module.exports = router;
15 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/routes/ping.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | router.get('/', function (req, res, next) {
5 | res.log.debug('we are being pinged');
6 | res.send('Hello, werfer!\n');
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/040_s3/werf.yaml:
--------------------------------------------------------------------------------
1 | project: werf-first-app
2 | configVersion: 1
3 |
4 | ---
5 | image: backend
6 | dockerfile: Dockerfile
7 | target: backend
8 |
9 | ---
10 | image: frontend
11 | dockerfile: Dockerfile
12 | target: frontend
13 |
--------------------------------------------------------------------------------
/2022/06-werf-nodejs/README.md:
--------------------------------------------------------------------------------
1 | A Node.js application used to demonstrate how you can build your app and deploy it
2 | to Kubernetes using [werf CI/CD tool](https://werf.io/).
3 |
4 | Each directory corresponds to the state of our Node.js application when we
5 | implement specific features in it:
6 |
7 | * [01_basic_app](01_basic_app/) is the initial, basic state of our app;
8 | * [020_assets](020_assets/) — when our app serves static assets;
9 | * [030_db](030_db/) — when we connect to MySQL database;
10 | * [040_s3](040_s3/) — when we use S3 storage for app's files.
11 |
12 | P.S. This code is heavily based on the relevant werf guide:
13 | [Node.js → Real-world apps](https://werf.io/guides/nodejs/200_real_apps.html).
14 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/01-template-data/01-pvc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: PersistentVolumeClaim
3 | metadata:
4 | name: pvc-template
5 | spec:
6 | accessModes:
7 | - ReadWriteOnce
8 | storageClassName: linstor-ssd-lvmthin-r2
9 | resources:
10 | requests:
11 | storage: 10Gi
12 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/01-template-data/02-pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: import-data
5 | annotations:
6 | spec:
7 | containers:
8 | - name: data-importer
9 | image: alpine:3.16
10 | command:
11 | - /bin/sh
12 | - -ec
13 | - |
14 | cat >/data/file.txt <<\EOT
15 | When you were here before
16 | Couldn't look you in the eye
17 | You're just like an angel
18 | Your skin makes me cry
19 | You float like a feather
20 | In a beautiful world
21 | I wish I was special
22 | You're so fuckin' special
23 | EOT
24 | echo "== data succefully imported into /data/file.txt"
25 | volumeMounts:
26 | - name: vol
27 | mountPath: /data
28 | terminationGracePeriodSeconds: 0
29 | volumes:
30 | - name: vol
31 | persistentVolumeClaim:
32 | claimName: pvc-template
33 | restartPolicy: Never
34 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/02-upgrade-app/01-pvc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: PersistentVolumeClaim
3 | metadata:
4 | name: mypvc
5 | spec:
6 | accessModes:
7 | - ReadWriteOnce
8 | storageClassName: linstor-ssd-lvmthin-r2
9 | resources:
10 | requests:
11 | storage: 10Gi
12 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/02-upgrade-app/02-pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: myapp-v10
5 | annotations:
6 | spec:
7 | containers:
8 | - name: myapp
9 | image: alpine:3.16
10 | command:
11 | - /bin/sh
12 | - -c
13 | - |
14 | echo "== version 1.0"
15 |
16 | echo "== list existing data"
17 | cat /data/file.txt
18 |
19 | echo "== running loop"
20 | while sleep 1; do
21 | date | tee -a /data/file.txt
22 | done
23 | volumeMounts:
24 | - name: vol
25 | mountPath: /data
26 | terminationGracePeriodSeconds: 0
27 | volumes:
28 | - name: vol
29 | persistentVolumeClaim:
30 | claimName: mypvc
31 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/02-upgrade-app/03-clonned-pvc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: PersistentVolumeClaim
3 | metadata:
4 | name: mypvc-test
5 | spec:
6 | storageClassName: linstor-ssd-lvmthin-r2
7 | dataSource:
8 | name: mypvc
9 | kind: PersistentVolumeClaim
10 | accessModes:
11 | - ReadWriteOnce
12 | resources:
13 | requests:
14 | storage: 10Gi
15 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/02-upgrade-app/04-pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: myapp-v11-test
5 | annotations:
6 | spec:
7 | containers:
8 | - name: myapp
9 | image: alpine:3.16
10 | command:
11 | - /bin/sh
12 | - -c
13 | - |
14 | uuid=$(cat /proc/sys/kernel/random/uuid)
15 |
16 | echo "== version 1.1"
17 |
18 | echo "== run migration"
19 | sed -i "/[^\t]/ s/^/${uuid}/" /data/file.txt
20 |
21 | echo "== list existing data"
22 | cat /data/file.txt
23 |
24 | echo "== running loop"
25 | while sleep 1; do
26 | echo -e "${uuid}\t$(date)" | tee -a /data/file.txt
27 | done
28 | volumeMounts:
29 | - name: vol
30 | mountPath: /data
31 | terminationGracePeriodSeconds: 0
32 | volumes:
33 | - name: vol
34 | persistentVolumeClaim:
35 | claimName: mypvc-test
36 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/02-upgrade-app/05-pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: myapp-v11-test
5 | annotations:
6 | spec:
7 | containers:
8 | - name: myapp
9 | image: alpine:3.16
10 | command:
11 | - /bin/sh
12 | - -c
13 | - |
14 | uuid=$(cat /proc/sys/kernel/random/uuid)
15 |
16 | echo "== version 1.1"
17 |
18 | echo "== run migration"
19 | sed -i "/[^\t]/ s/^/${uuid}\t/" /data/file.txt
20 |
21 | echo "== list existing data"
22 | cat /data/file.txt
23 |
24 | echo "== running loop"
25 | while sleep 1; do
26 | echo -e "${uuid}\t$(date)" | tee -a /data/file.txt
27 | done
28 | volumeMounts:
29 | - name: vol
30 | mountPath: /data
31 | terminationGracePeriodSeconds: 0
32 | volumes:
33 | - name: vol
34 | persistentVolumeClaim:
35 | claimName: mypvc-test
36 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/02-upgrade-app/06-snapshot.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: snapshot.storage.k8s.io/v1
2 | kind: VolumeSnapshot
3 | metadata:
4 | name: mypvc-before-upgrade
5 | spec:
6 | volumeSnapshotClassName: linstor
7 | source:
8 | persistentVolumeClaimName: mypvc
9 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/02-upgrade-app/07-pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: myapp-v11
5 | annotations:
6 | spec:
7 | containers:
8 | - name: myapp
9 | image: alpine:3.16
10 | command:
11 | - /bin/sh
12 | - -c
13 | - |
14 | uuid=$(cat /proc/sys/kernel/random/uuid)
15 |
16 | echo "== version 1.1"
17 |
18 | echo "== run migration"
19 | sed -i "/[^\t]/ s/^/${uuid}\t/" /data/file.txt
20 |
21 | echo "== list existing data"
22 | cat /data/file.txt
23 |
24 | echo "== running loop"
25 | while sleep 1; do
26 | echo -e "${uuid}\t$(date)" | tee -a /data/file.txt
27 | done
28 | volumeMounts:
29 | - name: vol
30 | mountPath: /data
31 | terminationGracePeriodSeconds: 0
32 | volumes:
33 | - name: vol
34 | persistentVolumeClaim:
35 | claimName: mypvc
36 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/02-upgrade-app/08-snapshot.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: snapshot.storage.k8s.io/v1
2 | kind: VolumeSnapshot
3 | metadata:
4 | name: mypvc-after-upgrade
5 | spec:
6 | volumeSnapshotClassName: linstor
7 | source:
8 | persistentVolumeClaimName: mypvc
9 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/02-upgrade-app/09-restore-snapshot.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: PersistentVolumeClaim
3 | metadata:
4 | name: mypvc
5 | spec:
6 | storageClassName: linstor-ssd-lvmthin-r2
7 | dataSource:
8 | name: mypvc-before-upgrade
9 | kind: VolumeSnapshot
10 | apiGroup: snapshot.storage.k8s.io
11 | accessModes:
12 | - ReadWriteOnce
13 | resources:
14 | requests:
15 | storage: 10Gi
16 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/03-backups-with-linstor/01-pvc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: PersistentVolumeClaim
3 | metadata:
4 | name: db-data
5 | spec:
6 | accessModes:
7 | - ReadWriteOnce
8 | storageClassName: linstor-ssd-lvmthin-r2
9 | resources:
10 | requests:
11 | storage: 10Gi
12 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/03-backups-with-linstor/02-pod.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: mydb
5 | annotations:
6 | spec:
7 | containers:
8 | - name: mydb
9 | image: alpine:3.16
10 | command:
11 | - /bin/sh
12 | - -c
13 | - |
14 | echo "== list existing data"
15 | cat /data/file.txt
16 |
17 | echo "== running loop"
18 | while sleep 1; do
19 | date | tee -a /data/file.txt
20 | done
21 | volumeMounts:
22 | - name: vol
23 | mountPath: /data
24 | terminationGracePeriodSeconds: 0
25 | volumes:
26 | - name: vol
27 | persistentVolumeClaim:
28 | claimName: db-data
29 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/03-backups-with-linstor/03-volume-snapshot-class.yaml:
--------------------------------------------------------------------------------
1 | kind: VolumeSnapshotClass
2 | apiVersion: snapshot.storage.k8s.io/v1
3 | metadata:
4 | name: linstor-minio
5 | driver: linstor.csi.linbit.com
6 | deletionPolicy: Retain
7 | parameters:
8 | snap.linstor.csi.linbit.com/type: S3
9 | snap.linstor.csi.linbit.com/remote-name: minio
10 | snap.linstor.csi.linbit.com/allow-incremental: "false"
11 | snap.linstor.csi.linbit.com/s3-bucket: foo
12 | snap.linstor.csi.linbit.com/s3-endpoint: XX.XXX.XX.XXX.nip.io
13 | snap.linstor.csi.linbit.com/s3-signing-region: minio
14 | snap.linstor.csi.linbit.com/s3-use-path-style: "true"
15 | csi.storage.k8s.io/snapshotter-secret-name: linstor-minio
16 | csi.storage.k8s.io/snapshotter-secret-namespace: minio
17 | ---
18 | kind: Secret
19 | apiVersion: v1
20 | metadata:
21 | name: linstor-minio
22 | namespace: minio
23 | immutable: true
24 | type: linstor.csi.linbit.com/s3-credentials.v1
25 | stringData:
26 | access-key: minio
27 | secret-key: minio123
28 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/03-backups-with-linstor/04-snapshot.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: snapshot.storage.k8s.io/v1
2 | kind: VolumeSnapshot
3 | metadata:
4 | name: mydb-backup1
5 | spec:
6 | volumeSnapshotClassName: linstor-minio
7 | source:
8 | persistentVolumeClaimName: db-data
9 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/03-backups-with-linstor/05-pvc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: PersistentVolumeClaim
3 | metadata:
4 | name: db-data-recovered
5 | spec:
6 | storageClassName: linstor-ssd-lvmthin-r2
7 | dataSource:
8 | name: mydb-backup1
9 | kind: VolumeSnapshot
10 | apiGroup: snapshot.storage.k8s.io
11 | accessModes:
12 | - ReadWriteOnce
13 | resources:
14 | requests:
15 | storage: 10Gi
16 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/03-backups-with-linstor/06-get-backups.sh:
--------------------------------------------------------------------------------
1 | linstor backup list minio
2 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/03-backups-with-linstor/07-volumesnapshotcontent.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: snapshot.storage.k8s.io/v1
2 | kind: VolumeSnapshotContent
3 | metadata:
4 | name: example-backup-from-s3
5 | spec:
6 | deletionPolicy: Delete
7 | driver: linstor.csi.linbit.com
8 | source:
9 | snapshotHandle: snapshot-9db78a7b-a9ba-4825-9009-673c7ebab043
10 | volumeSnapshotClassName: linstor-minio
11 | volumeSnapshotRef:
12 | apiVersion: snapshot.storage.k8s.io/v1
13 | kind: VolumeSnapshot
14 | name: example-backup-from-s3
15 | namespace: new-cluster
16 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/03-backups-with-linstor/08-volumesnapshot.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: snapshot.storage.k8s.io/v1
2 | kind: VolumeSnapshot
3 | metadata:
4 | name: example-backup-from-s3
5 | spec:
6 | source:
7 | volumeSnapshotContentName: example-backup-from-s3
8 | volumeSnapshotClassName: linstor-minio
9 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/03-backups-with-linstor/09-pvc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: PersistentVolumeClaim
3 | metadata:
4 | name: db-data
5 | spec:
6 | storageClassName: linstor-ssd-lvmthin-r2
7 | dataSource:
8 | name: example-backup-from-s3
9 | kind: VolumeSnapshot
10 | apiGroup: snapshot.storage.k8s.io
11 | accessModes:
12 | - ReadWriteOnce
13 | resources:
14 | requests:
15 | storage: 10Gi
16 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/03-backups-with-linstor/minio/01-minio-connect.sh:
--------------------------------------------------------------------------------
1 | kubectl port-forward -n minio svc/minio 9000:9000 40061:40061
2 |
--------------------------------------------------------------------------------
/2022/08-snapshot-controller/README.md:
--------------------------------------------------------------------------------
1 | Here are examples for using the snapshot-controller in Kubernetes.
--------------------------------------------------------------------------------
/2022/10-canary-example/.helm/templates/10-nginx-config.yaml:
--------------------------------------------------------------------------------
1 | {{ $name := printf "%s%s" (.Chart.Name) (.Values.global.canary_deploy) }}
2 | ---
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: {{ $name }}-configmap
7 | data:
8 | nginx.conf: |
9 | error_log /dev/stderr;
10 | events {
11 | worker_connections 100000;
12 | multi_accept on;
13 | }
14 | http {
15 | charset utf-8;
16 |
17 | server {
18 | listen 80;
19 | index index.html;
20 | root /app;
21 | error_log /dev/stderr;
22 | location / {
23 | try_files $uri /index.html$is_args$args;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/2022/10-canary-example/.helm/templates/30-ingress.yaml:
--------------------------------------------------------------------------------
1 | {{ $name := printf "%s%s" (.Chart.Name) (.Values.global.canary_deploy) }}
2 | apiVersion: networking.k8s.io/v1
3 | kind: Ingress
4 | metadata:
5 | name: {{ $name }}
6 | {{- if ne .Values.global.canary_deploy "" }}
7 | annotations:
8 | nginx.ingress.kubernetes.io/canary: "true"
9 | nginx.ingress.kubernetes.io/canary-weight: "30"
10 | # nginx.ingress.kubernetes.io/canary-by-header: {{ $.Values.global.canary_header | quote }}
11 | # nginx.ingress.kubernetes.io/canary-by-header-value: {{ $.Values.global.canary_header_value | quote }}
12 | {{- end }}
13 | spec:
14 | rules:
15 | - host: {{ pluck .Values.global.env .Values.nginx.url | first | default .Values.nginx.url._default }}
16 | http:
17 | paths:
18 | - path: "/"
19 | pathType: Prefix
20 | backend:
21 | service:
22 | name: {{ $name }}
23 | port:
24 | number: 80
--------------------------------------------------------------------------------
/2022/10-canary-example/.helm/values.yaml:
--------------------------------------------------------------------------------
1 | nginx:
2 | url:
3 | _default: "my.awesome.domain"
4 |
--------------------------------------------------------------------------------
/2022/10-canary-example/README.md:
--------------------------------------------------------------------------------
1 | Here are examples for implement canary releases in Kubernetes using Ingress.
--------------------------------------------------------------------------------
/2022/10-canary-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Wow! I'm canary nginx!
5 |
6 |
--------------------------------------------------------------------------------
/2022/10-canary-example/werf-giterminism.yaml:
--------------------------------------------------------------------------------
1 | giterminismConfigVersion: 1
2 | cli:
3 | allowCustomTags: true
4 | config:
5 | goTemplateRendering:
6 | allowEnvVariables:
7 | - CANARY_DEPLOY
8 | - CI_HELM_RELEASE
--------------------------------------------------------------------------------
/2022/10-canary-example/werf.yaml:
--------------------------------------------------------------------------------
1 | project: canary-example
2 | configVersion: 1
3 | deploy:
4 | helmRelease: '[[ project ]]{{ env "CI_HELM_RELEASE" }}'
5 | namespace: "[[ project ]]"
6 |
7 | ---
8 | image: nginx
9 | from: nginx:stable
10 | git:
11 | - add: /
12 | to: /app
13 | excludePaths:
14 | - .helm
15 | - werf.yaml
16 | - .getlab-ci.yml
--------------------------------------------------------------------------------
/2022/11-d8-user-authn/clean_up.sh:
--------------------------------------------------------------------------------
1 | kubectl delete dexprovider openldap-demo
2 | kubectl delete user openldap-demo
3 | kubectl delete ns openldap-demo
4 |
--------------------------------------------------------------------------------
/2022/11-d8-user-authn/dex-authenticator.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: deckhouse.io/v1
3 | kind: DexAuthenticator
4 | metadata:
5 | name: echoserver
6 | namespace: openldap-demo
7 | spec:
8 | applicationDomain: echo.{{ __cluster__domain__ }}
9 | sendAuthorizationHeader: false
10 | applicationIngressClassName: "nginx"
11 | allowedGroups:
12 | - developers
13 |
--------------------------------------------------------------------------------
/2022/11-d8-user-authn/dex-user.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: deckhouse.io/v1
3 | kind: User
4 | metadata:
5 | name: openldap-demo
6 | spec:
7 | email: openldap-demo@example.com
8 | # echo "bar" | htpasswd -BinC 10 "" | cut -d: -f2
9 | password: '$2a$10$spCnoGzDIRicDfiTmtImwu7sn2Csjj6oWRoLjNs6N/bV3WDsxioui'
10 | ttl: 2h
11 |
--------------------------------------------------------------------------------
/2022/11-redis-keydb/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM scratch
2 | COPY redis-latency /redis-latency
3 | CMD ["/redis-latency"]
--------------------------------------------------------------------------------
/2022/11-redis-keydb/README.md:
--------------------------------------------------------------------------------
1 | Here are examples for testing the Redis response.
2 |
3 | Application sets key in Redis-cluster every 1 second and export latency metrics in Prometheus format: max, min and average time of operation.
4 |
5 | Docker:
6 |
7 | ```
8 | docker pull trublast/redis-latency
9 | ```
10 |
11 | Build:
12 |
13 | ```
14 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags '-w' -o redis-latency *.go
15 | ```
--------------------------------------------------------------------------------
/2022/11-redis-keydb/go.mod:
--------------------------------------------------------------------------------
1 | module main.go
2 |
3 | go 1.19
4 |
5 | require (
6 | github.com/go-redis/redis/v8 v8.11.5
7 | github.com/prometheus/client_golang v1.13.1
8 | )
9 |
10 | require (
11 | github.com/beorn7/perks v1.0.1 // indirect
12 | github.com/cespare/xxhash/v2 v2.1.2 // indirect
13 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
14 | github.com/golang/protobuf v1.5.2 // indirect
15 | github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
16 | github.com/prometheus/client_model v0.2.0 // indirect
17 | github.com/prometheus/common v0.37.0 // indirect
18 | github.com/prometheus/procfs v0.8.0 // indirect
19 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
20 | google.golang.org/protobuf v1.28.1 // indirect
21 | )
22 |
--------------------------------------------------------------------------------
/2022/12-netflow/.helm/templates/dashboard.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: deckhouse.io/v1alpha1
3 | kind: GrafanaDashboardDefinition
4 | metadata:
5 | name: iptnetflow
6 | spec:
7 | folder: Applications
8 | definition: |
9 | {{ .Files.Get "dashboards/iptnetflow.json" | nindent 4 }}
10 | ---
11 | apiVersion: deckhouse.io/v1alpha1
12 | kind: GrafanaDashboardDefinition
13 | metadata:
14 | name: nodegraph
15 | spec:
16 | folder: Applications
17 | definition: |
18 | {{ .Files.Get "dashboards/nodegraph.json" | nindent 4 }}
--------------------------------------------------------------------------------
/2022/12-netflow/.helm/templates/datasource.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: deckhouse.io/v1
2 | kind: GrafanaAdditionalDatasource
3 | metadata:
4 | name: iptnetflow
5 | spec:
6 | access: Proxy
7 | jsonData:
8 | url: http://{{ .Chart.Name }}-nodegraph.{{ .Release.Namespace }}.svc.cluster.local:5000
9 | type: hamedkarbasi93-nodegraphapi-datasource
10 |
--------------------------------------------------------------------------------
/2022/12-netflow/.helm/templates/nodeconfig.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: deckhouse.io/v1alpha1
2 | kind: NodeGroupConfiguration
3 | metadata:
4 | name: iptnetflow.sh
5 | spec:
6 | weight: 100
7 | bundles:
8 | - "*"
9 | nodeGroups:
10 | - "*"
11 | content: |
12 | dpkg -s iptables-netflow-dkms || ( apt-get update && apt-get install -y iptables-netflow-dkms )
13 | lsmod | grep ipt_NETFLOW || modprobe ipt_NETFLOW protocol=9
14 | [ "$(sysctl -n net.netflow.destination)" = "{{ .Values.collector.ip }}:2055" ] || sysctl -w net.netflow.destination={{ .Values.collector.ip }}:2055
15 | [ "$(sysctl -n net.netflow.protocol)" = "9" ] || sysctl -w net.netflow.protocol=9
16 | iptables -C FORWARD -i cni0 -j NETFLOW || iptables -I FORWARD -i cni0 -j NETFLOW
17 |
--------------------------------------------------------------------------------
/2022/12-netflow/.helm/templates/rbac.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: iptnetflow-sa
6 | ---
7 | apiVersion: rbac.authorization.k8s.io/v1
8 | kind: ClusterRole
9 | metadata:
10 | name: iptnetflow
11 | rules:
12 | - apiGroups: [""]
13 | resources: ["pods"]
14 | verbs: ["get", "watch", "list"]
15 | - apiGroups: ["monitoring.coreos.com"]
16 | resourceNames: ["main","longterm"]
17 | resources: ["prometheuses/http"]
18 | verbs: ["get"]
19 | ---
20 | apiVersion: rbac.authorization.k8s.io/v1
21 | kind: ClusterRoleBinding
22 | metadata:
23 | name: iptnetflow
24 | roleRef:
25 | apiGroup: rbac.authorization.k8s.io
26 | kind: ClusterRole
27 | name: iptnetflow
28 | subjects:
29 | - kind: ServiceAccount
30 | name: iptnetflow-sa
31 | namespace: iptnetflow
32 |
--------------------------------------------------------------------------------
/2022/12-netflow/.helm/values.yaml:
--------------------------------------------------------------------------------
1 | collector:
2 | ip: 10.222.156.111
3 |
--------------------------------------------------------------------------------
/2023/03-monitoring/README.md:
--------------------------------------------------------------------------------
1 | # Steps
2 |
3 | ## Install VictoriaMetrics
4 |
5 | See [**victoria-metrics.txt**](./victoria-metrics.txt) for more details.
6 |
7 | ## Add PrometheusRemoteWrite
8 |
9 | * Apply [**prometheus-remote-write**](./prometheus-remote-write.yaml) manifests to the cluster.
10 |
11 | ## Add VictoriaMetrics datasource to Grafana
12 |
13 | * Apply [**grafana-additional-datasource**](./grafana-additional-datasource.yaml) manifests to the cluster.
14 | * Show Grafana `Explore` page and query metrics from VictoriaMetrics.
15 |
16 | ## Add an alert that is always firing
17 |
18 | * Apply [**custom-prometheus-rule**](./custom-prometheus-rule.yaml) manifests to the cluster.
19 | * Show the alerts page in Prometheus.
20 |
21 | ## Add a brand new Grafana dashboard
22 |
23 | * Apply [**grafana-dashboard-definition**](./grafana-dashboard-definition.yaml) manifests to the cluster.
24 | * Show the dashboards page in Grafana.
25 |
26 | ## Deploy Custom alertmanager
27 |
28 | * Apply [**alertmanager**](./alertmanager.yaml) manifests to the cluster.
29 | * Show the starting page of Alertmanager.
30 |
31 | # Cleaning
32 |
33 | Execute [**clean_up.sh**](./clean_up.sh)
34 |
--------------------------------------------------------------------------------
/2023/03-monitoring/alertmanager.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: deckhouse.io/v1alpha1
3 | kind: CustomAlertmanager
4 | metadata:
5 | name: telegram
6 | spec:
7 | type: Internal
8 | internal:
9 | route:
10 | groupBy: [ 'job' ]
11 | groupWait: 30s
12 | groupInterval: 5m
13 | repeatInterval: 12h
14 | receiver: 'telegram'
15 | receivers:
16 | - name: telegram
17 | telegramConfigs:
18 | - botToken:
19 | key: token
20 | name: telegram-bot-secret
21 | chatID: 111
22 |
--------------------------------------------------------------------------------
/2023/03-monitoring/clean_up.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Copyright 2022 Flant JSC
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | kubectl delete prometheusremotewrite victoria-metrics
18 | kubectl delete grafanaadditionaldatasource victoria-metrics
19 | kubectl delete customprometheusrules always-firing-alert
20 | kubectl delete grafanadashboarddefinitions up-services
21 | kubectl delete customalertmanager telegram
22 |
23 | helm uninstall victoria-metrics -n victoria-metrics-test
24 | kubectl delete ns victoria-metrics-test
25 |
--------------------------------------------------------------------------------
/2023/03-monitoring/custom-prometheus-rule.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: deckhouse.io/v1
3 | kind: CustomPrometheusRules
4 | metadata:
5 | name: always-firing-alert
6 | spec:
7 | groups:
8 | - name: cluster-state-alert.rules
9 | rules:
10 | - alert: PrometheusCanScrapeTragets
11 | annotations:
12 | description: This is a fake alert only for a demo.
13 | summary: The alers shows that Prometheus can scrape tragets.
14 | expr: |
15 | up{job="deckhouse"}
16 |
--------------------------------------------------------------------------------
/2023/03-monitoring/grafana-additional-datasource.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: deckhouse.io/v1
3 | kind: GrafanaAdditionalDatasource
4 | metadata:
5 | name: victoria-metrics
6 | spec:
7 | access: Proxy
8 | basicAuth: false
9 | jsonData:
10 | timeInterval: 30s
11 | type: prometheus
12 | url: http://victoria-metrics-victoria-metrics-single-server.victoria-metrics-test.svc.cluster.local:8428/
13 |
--------------------------------------------------------------------------------
/2023/03-monitoring/prometheus-remote-write.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: deckhouse.io/v1
3 | kind: PrometheusRemoteWrite
4 | metadata:
5 | name: victoria-metrics
6 | spec:
7 | url: http://victoria-metrics-victoria-metrics-single-server.victoria-metrics-test.svc.cluster.local:8428/api/v1/write
8 |
--------------------------------------------------------------------------------
/2023/03-monitoring/victoria-metrics.txt:
--------------------------------------------------------------------------------
1 | https://github.com/VictoriaMetrics/helm-charts
2 |
3 | $ helm repo add vm https://victoriametrics.github.io/helm-charts/
4 | $ helm repo update
5 | $ helm upgrade --install victoria-metrics vm/victoria-metrics-single --namespace victoria-metrics-test --create-namespace
6 |
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/.gitignore:
--------------------------------------------------------------------------------
1 | # ---> Go
2 | # If you prefer the allow list template instead of the deny list, see community template:
3 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
4 | #
5 | # Binaries for programs and plugins
6 | *.exe
7 | *.exe~
8 | *.dll
9 | *.so
10 | *.dylib
11 |
12 | # Test binary, built with `go test -c`
13 | *.test
14 |
15 | # Output of the go coverage tool, specifically when used with LiteIDE
16 | *.out
17 |
18 | # Dependency directories (remove the comment below to include it)
19 | # vendor/
20 |
21 | # Go workspace file
22 | go.work
23 |
24 | # IntellijIdea files
25 | *.iml
26 | .idea
27 | out
28 |
29 | *.DS_Store
30 | ._*
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/.helm/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | annotations:
5 | kubernetes.io/ingress.class: nginx
6 | name: habr-app
7 | spec:
8 | rules:
9 | - host: habrapp.example.com
10 | http:
11 | paths:
12 | - path: /
13 | pathType: Prefix
14 | backend:
15 | service:
16 | name: habr-app
17 | port:
18 | number: 8080
19 | tls:
20 | - hosts:
21 | - habrapp.example.com
22 | secretName: habr-app-tls
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/.helm/templates/job-db-setup-and-migrate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | # Версия Helm-релиза в имени Job заставит Job каждый раз пересоздаваться.
5 | # Так мы сможем обойти то, что Job неизменяема.
6 | name: "setup-and-migrate-db-rev{{ .Release.Revision }}"
7 | spec:
8 | backoffLimit: 0
9 | template:
10 | spec:
11 | restartPolicy: Never
12 | initContainers:
13 | - name: waiting-mysql
14 | image: alpine:3.6
15 | command: [ '/bin/sh', '-c', 'while ! nc -z mysql 3306; do sleep 1; done' ]
16 | containers:
17 | - name: setup-and-migrate-db
18 | image: {{ .Values.werf.image.app }}
19 | command: ["/migrations/migrate", "-database", "mysql://root:password@tcp(mysql:3306)/habr-app", "-path", "/migrations/schemes", "up"]
20 |
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/.helm/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: habr-app
5 | spec:
6 | selector:
7 | app: habr-app
8 | ports:
9 | - name: http
10 | port: 8080
11 |
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/Dockerfile:
--------------------------------------------------------------------------------
1 | # Используем многоступенчатую сборку образа (multi-stage build)
2 | # Образ, в котором будет собираться проект
3 | FROM golang:1.18-alpine AS build
4 | # Устанавливаем curl и tar.
5 | RUN apk add curl tar
6 | # Копируем исходники приложения
7 | COPY . /app
8 | WORKDIR /app
9 | # Скачиваем утилиту migrate и распаковываем полученный архив.
10 | RUN curl -L https://github.com/golang-migrate/migrate/releases/download/v4.16.2/migrate.linux-amd64.tar.gz | tar xvz
11 | # Запускаем загрузку нужных пакетов.
12 | RUN go mod download
13 | # Запускаем сборку приложения.
14 | RUN go build -o /goapp cmd/main.go
15 |
16 | # Образ, который будет разворачиваться в кластере.
17 | FROM alpine:latest
18 | WORKDIR /
19 | # Копируем из сборочного образа исполняемый файл проекта.
20 | COPY --from=build /goapp /goapp
21 | # Копируем из сборочного образа распакованный файл утилиты migrate и схемы миграции.
22 | COPY --from=build /app/migrate /migrations/migrate
23 | COPY db/migrations /migrations/schemes
24 | # Копируем файлы ассетов и шаблоны.
25 | COPY ./templates /templates
26 | EXPOSE 8080
27 | ENTRYPOINT ["/goapp"]
28 |
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/README.md:
--------------------------------------------------------------------------------
1 | # An application for deployment to a cluster managed by Deckhouse.
2 |
3 |
4 |
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "habr_app/internal/app"
4 |
5 | func main() {
6 | app.Run()
7 | }
8 |
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/db/migrations/000001_create_talkers_table.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS talkers;
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/db/migrations/000001_create_talkers_table.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE talkers (
2 | id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
3 | message TEXT NOT NULL,
4 | name TEXT NOT NULL
5 | );
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/internal/app/app.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "habr_app/internal/common"
5 | "habr_app/internal/controllers"
6 | "net/http"
7 |
8 | "github.com/gin-gonic/gin"
9 | )
10 |
11 | func Run() {
12 | route := gin.New()
13 | route.Use(gin.Recovery())
14 | route.Use(common.JsonLogger())
15 |
16 | route.LoadHTMLGlob("templates/*")
17 |
18 | route.GET("/", func(context *gin.Context) {
19 | context.HTML(http.StatusOK, "index.html", gin.H{})
20 | })
21 |
22 | route.GET("/remember", controllers.RememberController)
23 | route.GET("/say", controllers.SayController)
24 |
25 | err := route.Run()
26 | if err != nil {
27 | return
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/internal/common/json_logger_filter.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/gin-gonic/gin"
6 | )
7 |
8 | func JsonLogger() gin.HandlerFunc {
9 | return gin.LoggerWithFormatter(
10 | func(params gin.LogFormatterParams) string {
11 | log := make(map[string]interface{})
12 |
13 | log["status_code"] = params.StatusCode
14 | log["path"] = params.Path
15 | log["method"] = params.Method
16 | log["start_time"] = params.TimeStamp.Format("2023/01/02 - 13:04:05")
17 | log["remote_addr"] = params.ClientIP
18 | log["response_time"] = params.Latency.String()
19 |
20 | str, _ := json.Marshal(log)
21 | return string(str) + "\n"
22 | },
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/internal/services/db_service.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import "os"
4 |
5 | func GetDBCredentials() (string, string) {
6 | dbType := os.Getenv("DB_TYPE")
7 | dbName := os.Getenv("DB_NAME")
8 | dbUser := os.Getenv("DB_USER")
9 | dbPasswd := os.Getenv("DB_PASSWD")
10 | dbHost := os.Getenv("DB_HOST")
11 | dbPort := os.Getenv("DB_PORT")
12 | return dbType, dbUser + ":" + dbPasswd + "@tcp(" + dbHost + ":" + dbPort + ")/" + dbName
13 | }
14 |
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/templates/remember.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Deckhouse and werf demo
7 |
8 |
9 |
10 |
11 |
12 | Hello, {{ .Name }}. You message "{{ .Message }}" has been saved.
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/2023/08-deckhouse-werf/werf.yaml:
--------------------------------------------------------------------------------
1 | project: habr-app
2 | configVersion: 1
3 |
4 | ---
5 | image: app
6 | dockerfile: Dockerfile
7 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/.gitignore:
--------------------------------------------------------------------------------
1 | # ---> Go
2 | # If you prefer the allow list template instead of the deny list, see community template:
3 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
4 | #
5 | # Binaries for programs and plugins
6 | *.exe
7 | *.exe~
8 | *.dll
9 | *.so
10 | *.dylib
11 |
12 | # Test binary, built with `go test -c`
13 | *.test
14 |
15 | # Output of the go coverage tool, specifically when used with LiteIDE
16 | *.out
17 |
18 | # Dependency directories (remove the comment below to include it)
19 | # vendor/
20 |
21 | # Go workspace file
22 | go.work
23 |
24 | # IntellijIdea files
25 | *.iml
26 | .idea
27 | out
28 |
29 | *.DS_Store
30 | ._*
31 |
32 | mysql
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/.helm/templates/deployment-backend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: app-backend
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: app-backend
10 | template:
11 | metadata:
12 | labels:
13 | app: app-backend
14 | spec:
15 | imagePullSecrets:
16 | - name: registrysecret
17 | containers:
18 | - name: app-backend
19 | image: {{ .Values.werf.image.backend }}
20 | ports:
21 | - containerPort: 8080
22 | env:
23 | - name: GIN_MODE
24 | value: "release"
25 | - name: DB_TYPE
26 | value: "mysql"
27 | - name: DB_NAME
28 | value: "app"
29 | - name: DB_USER
30 | value: "root"
31 | - name: DB_PASSWD
32 | value: "password"
33 | - name: DB_HOST
34 | value: "mysql"
35 | - name: DB_PORT
36 | value: "3306"
37 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/.helm/templates/deployment-frontend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: app-frontend
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: app-frontend
10 | template:
11 | metadata:
12 | labels:
13 | app: app-frontend
14 | spec:
15 | imagePullSecrets:
16 | - name: registrysecret
17 | containers:
18 | - name: app-frontend
19 | image: {{ .Values.werf.image.frontend }}
20 | ports:
21 | - containerPort: 80
22 | ---
23 | apiVersion: v1
24 | kind: Service
25 | metadata:
26 | name: app-frontend
27 | spec:
28 | selector:
29 | app: app-frontend
30 | ports:
31 | - name: http
32 | port: 80
33 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/.helm/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | annotations:
5 | kubernetes.io/ingress.class: nginx
6 | name: app
7 | spec:
8 | rules:
9 | - host: werf-guide-app.test
10 | http:
11 | paths:
12 | - path: /
13 | pathType: Prefix
14 | backend:
15 | service:
16 | name: app-frontend
17 | port:
18 | number: 80
19 | - path: /api
20 | pathType: Prefix
21 | backend:
22 | service:
23 | name: app-backend
24 | port:
25 | number: 8080
26 | tls:
27 | - hosts:
28 | - werf-guide-app.test
29 | secretName: app-tls
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/.helm/templates/job-db-setup-and-migrate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | # Версия Helm-релиза в имени Job заставит Job каждый раз пересоздаваться.
5 | # Так мы сможем обойти то, что Job неизменяема.
6 | name: "setup-and-migrate-db-rev{{ .Release.Revision }}"
7 | spec:
8 | backoffLimit: 0
9 | template:
10 | spec:
11 | restartPolicy: Never
12 | initContainers:
13 | - name: waiting-mysql
14 | image: alpine:3.6
15 | command: [ '/bin/sh', '-c', 'while ! nc -z mysql 3306; do sleep 1; done' ]
16 | containers:
17 | - name: setup-and-migrate-db
18 | image: {{ .Values.werf.image.backend }}
19 | command: ["/migrations/migrate", "-database", "mysql://root:password@tcp(mysql:3306)/app", "-path", "/migrations/schemes", "up"]
20 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/.helm/templates/service-backend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: app-backend
5 | spec:
6 | selector:
7 | app: app-backend
8 | ports:
9 | - name: http
10 | port: 8080
11 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/README.md:
--------------------------------------------------------------------------------
1 | # An application for organizing a local stand with werf
2 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/backend/Dockerfile:
--------------------------------------------------------------------------------
1 | # Используем многоступенчатую сборку образа (multi-stage build)
2 | # Образ, в котором будет собираться проект
3 | FROM golang:1.18-alpine AS build
4 | # Устанавливаем curl и tar.
5 | RUN apk add curl tar
6 | # Копируем исходники приложения
7 | COPY backend/. /app
8 | WORKDIR /app
9 | # Скачиваем утилиту migrate и распаковываем полученный архив.
10 | RUN curl -L https://github.com/golang-migrate/migrate/releases/download/v4.16.2/migrate.linux-amd64.tar.gz | tar xvz
11 | # Запускаем загрузку нужных пакетов.
12 | RUN go mod download
13 | # Запускаем сборку приложения.
14 | RUN go build -o /goapp cmd/main.go
15 |
16 | # Образ, который будет разворачиваться в кластере.
17 | FROM alpine:latest
18 | WORKDIR /
19 | # Копируем из сборочного образа исполняемый файл проекта.
20 | COPY --from=build /goapp /goapp
21 | # Копируем из сборочного образа распакованный файл утилиты migrate и схемы миграции.
22 | COPY --from=build /app/migrate /migrations/migrate
23 | COPY backend/db/migrations /migrations/schemes
24 | EXPOSE 8080
25 | ENTRYPOINT ["/goapp"]
26 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/backend/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "app/internal/app"
4 |
5 | func main() {
6 | app.Run()
7 | }
8 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/backend/db/migrations/000001_create_talkers_table.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS talkers;
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/backend/db/migrations/000001_create_talkers_table.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE talkers (
2 | id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
3 | message TEXT NOT NULL,
4 | name TEXT NOT NULL
5 | );
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/backend/internal/app/app.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "app/internal/common"
5 | "app/internal/controllers"
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | func Run() {
10 | route := gin.New()
11 | route.Use(gin.Recovery())
12 | route.Use(common.JsonLogger())
13 |
14 | route.GET("/api/remember", controllers.RememberController)
15 | route.GET("/api/say", controllers.SayController)
16 |
17 | err := route.Run()
18 | if err != nil {
19 | return
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/backend/internal/common/json_logger_filter.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/gin-gonic/gin"
6 | )
7 |
8 | func JsonLogger() gin.HandlerFunc {
9 | return gin.LoggerWithFormatter(
10 | func(params gin.LogFormatterParams) string {
11 | log := make(map[string]interface{})
12 |
13 | log["status_code"] = params.StatusCode
14 | log["path"] = params.Path
15 | log["method"] = params.Method
16 | log["start_time"] = params.TimeStamp.Format("2023/01/02 - 13:04:05")
17 | log["remote_addr"] = params.ClientIP
18 | log["response_time"] = params.Latency.String()
19 |
20 | str, _ := json.Marshal(log)
21 | return string(str) + "\n"
22 | },
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/backend/internal/services/db_service.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import "os"
4 |
5 | func GetDBCredentials() (string, string) {
6 | dbType := os.Getenv("DB_TYPE")
7 | dbName := os.Getenv("DB_NAME")
8 | dbUser := os.Getenv("DB_USER")
9 | dbPasswd := os.Getenv("DB_PASSWD")
10 | dbHost := os.Getenv("DB_HOST")
11 | dbPort := os.Getenv("DB_PORT")
12 | return dbType, dbUser + ":" + dbPasswd + "@tcp(" + dbHost + ":" + dbPort + ")/" + dbName
13 | }
14 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | # этап сборки (build stage)
2 | FROM node:lts-alpine as build-stage
3 | WORKDIR /app
4 | COPY frontend/package*.json ./
5 | RUN npm install
6 | COPY frontend/. .
7 | RUN npm run build
8 |
9 | # этап production (production-stage)
10 | FROM nginx:stable-alpine as production-stage
11 | COPY --from=build-stage /app/dist /usr/share/nginx/html
12 | EXPOSE 80
13 | CMD ["nginx", "-g", "daemon off;"]
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/README.md:
--------------------------------------------------------------------------------
1 | # frontend
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Lints and fixes files
19 | ```
20 | npm run lint
21 | ```
22 |
23 | ### Customize configuration
24 | See [Configuration Reference](https://cli.vuejs.org/config/).
25 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "baseUrl": "./",
6 | "moduleResolution": "node",
7 | "paths": {
8 | "@/*": [
9 | "src/*"
10 | ]
11 | },
12 | "lib": [
13 | "esnext",
14 | "dom",
15 | "dom.iterable",
16 | "scripthost"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "axios": "^1.6.0",
12 | "bootstrap": "^5.3.1",
13 | "core-js": "^3.8.3",
14 | "vue": "^3.2.13"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.12.16",
18 | "@babel/eslint-parser": "^7.12.16",
19 | "@vue/cli-plugin-babel": "~5.0.0",
20 | "@vue/cli-plugin-eslint": "~5.0.0",
21 | "@vue/cli-service": "~5.0.0",
22 | "eslint": "^7.32.0",
23 | "eslint-plugin-vue": "^8.0.3"
24 | },
25 | "eslintConfig": {
26 | "root": true,
27 | "env": {
28 | "node": true
29 | },
30 | "extends": [
31 | "plugin:vue/vue3-essential",
32 | "eslint:recommended"
33 | ],
34 | "parserOptions": {
35 | "parser": "@babel/eslint-parser"
36 | },
37 | "rules": {}
38 | },
39 | "browserslist": [
40 | "> 1%",
41 | "last 2 versions",
42 | "not dead",
43 | "not ie 11"
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2023/08-werf-local-dev/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
16 |
19 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2023/08-werf-local-dev/frontend/src/assets/logo.png
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 |
4 | createApp(App).mount('#app')
5 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/frontend/vue.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('@vue/cli-service')
2 | module.exports = defineConfig({
3 | transpileDependencies: true
4 | })
5 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {}
6 | }
7 |
--------------------------------------------------------------------------------
/2023/08-werf-local-dev/werf.yaml:
--------------------------------------------------------------------------------
1 | project: app
2 | configVersion: 1
3 |
4 | ---
5 | image: backend
6 | dockerfile: backend/Dockerfile
7 |
8 | ---
9 | image: frontend
10 | dockerfile: frontend/Dockerfile
11 |
12 | ---
13 | image: frontend-compose
14 | dockerfile: frontend/Dockerfile
15 | target: build-stage
16 |
--------------------------------------------------------------------------------
/2023/11-github-actions/.github/workflows/production_deployment.yml:
--------------------------------------------------------------------------------
1 | name: Production Deployment
2 | on:
3 | push:
4 | branches: [main]
5 | jobs:
6 |
7 | converge:
8 | name: Converge
9 | runs-on: ubuntu-latest
10 | steps:
11 |
12 | - name: Checkout code
13 | uses: actions/checkout@v3
14 | with:
15 | fetch-depth: 0
16 |
17 | - name: Converge
18 | uses: werf/actions/converge@v1.2
19 | with:
20 | env: production
21 | kube-config-base64-data: ${{ secrets.KUBE_CONFIG_BASE64_DATA }}
22 | env:
23 | WERF_SET_ENV_URL: "envUrl=http://habrapp.example.com"
24 |
25 |
--------------------------------------------------------------------------------
/2023/11-github-actions/.gitignore:
--------------------------------------------------------------------------------
1 | # ---> Go
2 | # If you prefer the allow list template instead of the deny list, see community template:
3 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
4 | #
5 | # Binaries for programs and plugins
6 | *.exe
7 | *.exe~
8 | *.dll
9 | *.so
10 | *.dylib
11 |
12 | # Test binary, built with `go test -c`
13 | *.test
14 |
15 | # Output of the go coverage tool, specifically when used with LiteIDE
16 | *.out
17 |
18 | # Dependency directories (remove the comment below to include it)
19 | # vendor/
20 |
21 | # Go workspace file
22 | go.work
23 |
24 | # IntellijIdea files
25 | *.iml
26 | .idea
27 | out
28 |
29 | *.DS_Store
30 | ._*
31 |
32 | mysql
--------------------------------------------------------------------------------
/2023/11-github-actions/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | stages:
2 | - build
3 | - deploy
4 | - dismiss
5 | - cleanup
6 |
7 | before_script:
8 | - type trdl && . $(trdl use werf 1.2 stable)
9 | - type werf && source $(werf ci-env gitlab --as-file)
10 |
11 | Build and Publish:
12 | stage: build
13 | script:
14 | - werf build
15 | except: [schedules]
16 | tags: [werf]
17 |
18 | .base_deploy:
19 | stage: deploy
20 | script:
21 | - werf converge --skip-build --set "env_url=$(echo ${CI_ENVIRONMENT_URL} | cut -d / -f 3)"
22 | except: [schedules]
23 | tags: [werf]
24 |
25 | Deploy to Production:
26 | extends: .base_deploy
27 | environment:
28 | name: production
29 | url: https://habrapp.zhbert.ru
30 | only: [main]
31 |
--------------------------------------------------------------------------------
/2023/11-github-actions/.helm/templates/deployment-backend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: habr-app-backend
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: habr-app-backend
10 | template:
11 | metadata:
12 | labels:
13 | app: habr-app-backend
14 | spec:
15 | imagePullSecrets:
16 | - name: registrysecret
17 | containers:
18 | - name: app-backend
19 | image: {{ .Values.werf.image.backend }}
20 | ports:
21 | - containerPort: 8080
22 | env:
23 | - name: GIN_MODE
24 | value: "release"
25 | - name: DB_TYPE
26 | value: "mysql"
27 | - name: DB_NAME
28 | value: "habr-app"
29 | - name: DB_USER
30 | value: "root"
31 | - name: DB_PASSWD
32 | value: "password"
33 | - name: DB_HOST
34 | value: "mysql"
35 | - name: DB_PORT
36 | value: "3306"
37 |
--------------------------------------------------------------------------------
/2023/11-github-actions/.helm/templates/deployment-frontend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: habr-app-frontend
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: habr-app-frontend
10 | template:
11 | metadata:
12 | labels:
13 | app: habr-app-frontend
14 | spec:
15 | imagePullSecrets:
16 | - name: registrysecret
17 | containers:
18 | - name: app-frontend
19 | image: {{ .Values.werf.image.frontend }}
20 | ports:
21 | - containerPort: 80
22 | ---
23 | apiVersion: v1
24 | kind: Service
25 | metadata:
26 | name: habr-app-frontend
27 | spec:
28 | selector:
29 | app: habr-app-frontend
30 | ports:
31 | - name: http
32 | port: 80
33 |
--------------------------------------------------------------------------------
/2023/11-github-actions/.helm/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | annotations:
5 | kubernetes.io/ingress.class: nginx
6 | name: habr-app
7 | spec:
8 | rules:
9 | - host: habrapp.zhbert.ru
10 | http:
11 | paths:
12 | - path: /
13 | pathType: Prefix
14 | backend:
15 | service:
16 | name: habr-app-frontend
17 | port:
18 | number: 80
19 | - path: /api
20 | pathType: Prefix
21 | backend:
22 | service:
23 | name: habr-app-backend
24 | port:
25 | number: 8080
26 | tls:
27 | - hosts:
28 | - habrapp.example.com
29 | secretName: habr-app-tls
30 |
--------------------------------------------------------------------------------
/2023/11-github-actions/.helm/templates/job-db-setup-and-migrate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | # Версия Helm-релиза в имени Job заставит Job каждый раз пересоздаваться.
5 | # Так мы сможем обойти то, что Job неизменяема.
6 | name: "setup-and-migrate-db-rev{{ .Release.Revision }}"
7 | spec:
8 | backoffLimit: 0
9 | template:
10 | spec:
11 | restartPolicy: Never
12 | initContainers:
13 | - name: waiting-mysql
14 | image: alpine:3.6
15 | command: [ '/bin/sh', '-c', 'while ! nc -z mysql 3306; do sleep 1; done' ]
16 | containers:
17 | - name: setup-and-migrate-db
18 | image: {{ .Values.werf.image.backend }}
19 | command: ["/migrations/migrate", "-database", "mysql://root:password@tcp(mysql:3306)/habr-app", "-path", "/migrations/schemes", "up"]
20 |
--------------------------------------------------------------------------------
/2023/11-github-actions/.helm/templates/service-backend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: habr-app-backend
5 | spec:
6 | selector:
7 | app: habr-app-backend
8 | ports:
9 | - name: http
10 | port: 8080
11 |
--------------------------------------------------------------------------------
/2023/11-github-actions/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Konstantin Nezhbert
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/2023/11-github-actions/backend/Dockerfile:
--------------------------------------------------------------------------------
1 | # Используем многоступенчатую сборку образа (multi-stage build)
2 | # Образ, в котором будет собираться проект
3 | FROM golang:1.18-alpine AS build
4 | # Устанавливаем curl и tar.
5 | RUN apk add curl tar
6 | # Копируем исходники приложения
7 | COPY backend/. /app
8 | WORKDIR /app
9 | # Скачиваем утилиту migrate и распаковываем полученный архив.
10 | RUN curl -L https://github.com/golang-migrate/migrate/releases/download/v4.16.2/migrate.linux-amd64.tar.gz | tar xvz
11 | # Запускаем загрузку нужных пакетов.
12 | RUN go mod download
13 | # Запускаем сборку приложения.
14 | RUN go build -o /goapp cmd/main.go
15 |
16 | # Образ, который будет разворачиваться в кластере.
17 | FROM alpine:latest
18 | WORKDIR /
19 | # Копируем из сборочного образа исполняемый файл проекта.
20 | COPY --from=build /goapp /goapp
21 | # Копируем из сборочного образа распакованный файл утилиты migrate и схемы миграции.
22 | COPY --from=build /app/migrate /migrations/migrate
23 | COPY backend/db/migrations /migrations/schemes
24 | EXPOSE 8080
25 | ENTRYPOINT ["/goapp"]
26 |
--------------------------------------------------------------------------------
/2023/11-github-actions/backend/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "habr_app/internal/app"
4 |
5 | func main() {
6 | app.Run()
7 | }
8 |
--------------------------------------------------------------------------------
/2023/11-github-actions/backend/db/migrations/000001_create_talkers_table.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS talkers;
--------------------------------------------------------------------------------
/2023/11-github-actions/backend/db/migrations/000001_create_talkers_table.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE talkers (
2 | id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
3 | message TEXT NOT NULL,
4 | name TEXT NOT NULL
5 | );
--------------------------------------------------------------------------------
/2023/11-github-actions/backend/internal/app/app.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "habr_app/internal/common"
6 | "habr_app/internal/controllers"
7 | )
8 |
9 | func Run() {
10 | route := gin.New()
11 | route.Use(gin.Recovery())
12 | route.Use(common.JsonLogger())
13 |
14 | route.GET("/api/remember", controllers.RememberController)
15 | route.GET("/api/say", controllers.SayController)
16 |
17 | err := route.Run()
18 | if err != nil {
19 | return
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/2023/11-github-actions/backend/internal/common/json_logger_filter.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/gin-gonic/gin"
6 | )
7 |
8 | func JsonLogger() gin.HandlerFunc {
9 | return gin.LoggerWithFormatter(
10 | func(params gin.LogFormatterParams) string {
11 | log := make(map[string]interface{})
12 |
13 | log["status_code"] = params.StatusCode
14 | log["path"] = params.Path
15 | log["method"] = params.Method
16 | log["start_time"] = params.TimeStamp.Format("2023/01/02 - 13:04:05")
17 | log["remote_addr"] = params.ClientIP
18 | log["response_time"] = params.Latency.String()
19 |
20 | str, _ := json.Marshal(log)
21 | return string(str) + "\n"
22 | },
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/2023/11-github-actions/backend/internal/services/db_service.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import "os"
4 |
5 | func GetDBCredentials() (string, string) {
6 | dbType := os.Getenv("DB_TYPE")
7 | dbName := os.Getenv("DB_NAME")
8 | dbUser := os.Getenv("DB_USER")
9 | dbPasswd := os.Getenv("DB_PASSWD")
10 | dbHost := os.Getenv("DB_HOST")
11 | dbPort := os.Getenv("DB_PORT")
12 | return dbType, dbUser + ":" + dbPasswd + "@tcp(" + dbHost + ":" + dbPort + ")/" + dbName
13 | }
14 |
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | # этап сборки (build stage)
2 | FROM node:lts-alpine as build-stage
3 | WORKDIR /app
4 | COPY frontend/package*.json ./
5 | RUN npm install
6 | COPY frontend/. .
7 | RUN npm run build
8 |
9 | # этап production (production-stage)
10 | FROM nginx:stable-alpine as production-stage
11 | COPY --from=build-stage /app/dist /usr/share/nginx/html
12 | EXPOSE 80
13 | CMD ["nginx", "-g", "daemon off;"]
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/README.md:
--------------------------------------------------------------------------------
1 | # frontend
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Lints and fixes files
19 | ```
20 | npm run lint
21 | ```
22 |
23 | ### Customize configuration
24 | See [Configuration Reference](https://cli.vuejs.org/config/).
25 |
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "baseUrl": "./",
6 | "moduleResolution": "node",
7 | "paths": {
8 | "@/*": [
9 | "src/*"
10 | ]
11 | },
12 | "lib": [
13 | "esnext",
14 | "dom",
15 | "dom.iterable",
16 | "scripthost"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "axios": "^1.6.0",
12 | "bootstrap": "^5.3.1",
13 | "core-js": "^3.8.3",
14 | "vue": "^3.2.13"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.12.16",
18 | "@babel/eslint-parser": "^7.12.16",
19 | "@vue/cli-plugin-babel": "~5.0.0",
20 | "@vue/cli-plugin-eslint": "~5.0.0",
21 | "@vue/cli-service": "~5.0.0",
22 | "eslint": "^7.32.0",
23 | "eslint-plugin-vue": "^8.0.3"
24 | },
25 | "eslintConfig": {
26 | "root": true,
27 | "env": {
28 | "node": true
29 | },
30 | "extends": [
31 | "plugin:vue/vue3-essential",
32 | "eslint:recommended"
33 | ],
34 | "parserOptions": {
35 | "parser": "@babel/eslint-parser"
36 | },
37 | "rules": {}
38 | },
39 | "browserslist": [
40 | "> 1%",
41 | "last 2 versions",
42 | "not dead",
43 | "not ie 11"
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2023/11-github-actions/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
16 |
19 |
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2023/11-github-actions/frontend/src/assets/logo.png
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 |
4 | createApp(App).mount('#app')
5 |
--------------------------------------------------------------------------------
/2023/11-github-actions/frontend/vue.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('@vue/cli-service')
2 | module.exports = defineConfig({
3 | transpileDependencies: true
4 | })
5 |
--------------------------------------------------------------------------------
/2023/11-github-actions/werf.yaml:
--------------------------------------------------------------------------------
1 | project: habr-app
2 | configVersion: 1
3 |
4 | ---
5 | image: backend
6 | dockerfile: backend/Dockerfile
7 |
8 | ---
9 | image: frontend
10 | dockerfile: frontend/Dockerfile
11 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/.gitignore:
--------------------------------------------------------------------------------
1 | # ---> Go
2 | # If you prefer the allow list template instead of the deny list, see community template:
3 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
4 | #
5 | # Binaries for programs and plugins
6 | *.exe
7 | *.exe~
8 | *.dll
9 | *.so
10 | *.dylib
11 |
12 | # Test binary, built with `go test -c`
13 | *.test
14 |
15 | # Output of the go coverage tool, specifically when used with LiteIDE
16 | *.out
17 |
18 | # Dependency directories (remove the comment below to include it)
19 | # vendor/
20 |
21 | # Go workspace file
22 | go.work
23 |
24 | # IntellijIdea files
25 | *.iml
26 | .idea
27 | out
28 |
29 | *.DS_Store
30 | ._*
31 |
32 | mysql
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | stages:
2 | - build
3 | - deploy
4 | - dismiss
5 | - cleanup
6 |
7 | before_script:
8 | - type trdl && . $(trdl use werf 1.2 stable)
9 | - type werf && source $(werf ci-env gitlab --as-file)
10 |
11 | Build and Publish:
12 | stage: build
13 | script:
14 | - werf build
15 | except: [schedules]
16 | tags: [werf]
17 |
18 | .base_deploy:
19 | stage: deploy
20 | script:
21 | - werf converge --require-built-images --set "env_url=$(echo ${CI_ENVIRONMENT_URL} | cut -d / -f 3)"
22 | except: [schedules]
23 | tags: [werf]
24 |
25 | Deploy to Production:
26 | extends: .base_deploy
27 | environment:
28 | name: production
29 | url: https://app.zhbert.ru
30 | only: [main]
31 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/.helm/templates/deployment-backend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: app-backend
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: app-backend
10 | template:
11 | metadata:
12 | labels:
13 | app: app-backend
14 | spec:
15 | imagePullSecrets:
16 | - name: registrysecret
17 | containers:
18 | - name: app-backend
19 | image: {{ .Values.werf.image.backend }}
20 | ports:
21 | - containerPort: 8080
22 | env:
23 | - name: GIN_MODE
24 | value: "release"
25 | - name: DB_TYPE
26 | value: "mysql"
27 | - name: DB_NAME
28 | value: "app"
29 | - name: DB_USER
30 | value: "root"
31 | - name: DB_PASSWD
32 | value: "password"
33 | - name: DB_HOST
34 | value: "mysql"
35 | - name: DB_PORT
36 | value: "3306"
37 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/.helm/templates/deployment-frontend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: app-frontend
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: app-frontend
10 | template:
11 | metadata:
12 | labels:
13 | app: app-frontend
14 | spec:
15 | imagePullSecrets:
16 | - name: registrysecret
17 | containers:
18 | - name: app-frontend
19 | image: {{ .Values.werf.image.frontend }}
20 | ports:
21 | - containerPort: 80
22 | ---
23 | apiVersion: v1
24 | kind: Service
25 | metadata:
26 | name: app-frontend
27 | spec:
28 | selector:
29 | app: app-frontend
30 | ports:
31 | - name: http
32 | port: 80
33 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/.helm/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: app
5 | spec:
6 | ingressClassName: nginx
7 | rules:
8 | - host: app.zhbert.ru
9 | http:
10 | paths:
11 | - path: /
12 | pathType: Prefix
13 | backend:
14 | service:
15 | name: app-frontend
16 | port:
17 | number: 80
18 | - path: /api
19 | pathType: Prefix
20 | backend:
21 | service:
22 | name: app-backend
23 | port:
24 | number: 8080
25 | tls:
26 | - hosts:
27 | - app.zhbert.ru
28 | secretName: app-tls
29 | ---
30 | apiVersion: cert-manager.io/v1
31 | kind: Certificate
32 | metadata:
33 | name: app
34 | namespace: app
35 | spec:
36 | secretName: app-tls
37 | issuerRef:
38 | kind: ClusterIssuer
39 | name: letsencrypt
40 | dnsNames:
41 | - app.zhbert.ru
42 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/.helm/templates/job-db-setup-and-migrate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | # Версия Helm-релиза в имени Job заставит Job каждый раз пересоздаваться.
5 | # Так мы сможем обойти то, что Job неизменяема.
6 | name: "setup-and-migrate-db-rev{{ .Release.Revision }}"
7 | spec:
8 | backoffLimit: 0
9 | template:
10 | spec:
11 | restartPolicy: Never
12 | initContainers:
13 | - name: waiting-mysql
14 | image: alpine:3.19
15 | command: [ '/bin/sh', '-c', 'while ! nc -z mysql 3306; do sleep 1; done' ]
16 | containers:
17 | - name: setup-and-migrate-db
18 | image: {{ .Values.werf.image.backend }}
19 | command: ["/migrations/migrate", "-database", "mysql://root:password@tcp(mysql:3306)/app", "-path", "/migrations/schemes", "up"]
20 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/.helm/templates/kube-pull-secret.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: registrysecret
5 | data:
6 | .dockerconfigjson: ewoJImF1dGhzIjogewoJCSJnaXRsYWIuemhiZXJ0LnJ1OjUwNTAiOiB7CgkJCSJhdXRoIjogImEzVmlaUzF3ZFd4c09tZHNaSFF0ZFcxU2VsbGhSMUpCU200dGIybzNaQzEzVG5rPSIKCQl9Cgl9Cn0=
7 | type: kubernetes.io/dockerconfigjson
8 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/.helm/templates/service-backend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: app-backend
5 | spec:
6 | selector:
7 | app: app-backend
8 | ports:
9 | - name: http
10 | port: 8080
11 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/README.md:
--------------------------------------------------------------------------------
1 | # An application for organizing a CI/CD pipeline with werf
2 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/backend/Dockerfile:
--------------------------------------------------------------------------------
1 | # Используем многоступенчатую сборку образа (multi-stage build)
2 | # Образ, в котором будет собираться проект
3 | FROM golang:1.18-alpine AS build
4 | # Устанавливаем curl и tar.
5 | RUN apk add curl tar
6 | # Копируем исходники приложения
7 | COPY backend/. /app
8 | WORKDIR /app
9 | # Скачиваем утилиту migrate и распаковываем полученный архив.
10 | RUN curl -L https://github.com/golang-migrate/migrate/releases/download/v4.16.2/migrate.linux-amd64.tar.gz | tar xvz
11 | # Запускаем загрузку нужных пакетов.
12 | RUN go mod download
13 | # Запускаем сборку приложения.
14 | RUN go build -o /goapp cmd/main.go
15 |
16 | # Образ, который будет разворачиваться в кластере.
17 | FROM alpine:latest
18 | WORKDIR /
19 | # Копируем из сборочного образа исполняемый файл проекта.
20 | COPY --from=build /goapp /goapp
21 | # Копируем из сборочного образа распакованный файл утилиты migrate и схемы миграции.
22 | COPY --from=build /app/migrate /migrations/migrate
23 | COPY backend/db/migrations /migrations/schemes
24 | EXPOSE 8080
25 | ENTRYPOINT ["/goapp"]
26 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/backend/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "app/internal/app"
4 |
5 | func main() {
6 | app.Run()
7 | }
8 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/backend/db/migrations/000001_create_talkers_table.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS talkers;
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/backend/db/migrations/000001_create_talkers_table.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE talkers (
2 | id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
3 | message TEXT NOT NULL,
4 | name TEXT NOT NULL
5 | );
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/backend/internal/app/app.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "app/internal/common"
5 | "app/internal/controllers"
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | func Run() {
10 | route := gin.New()
11 | route.Use(gin.Recovery())
12 | route.Use(common.JsonLogger())
13 |
14 | route.GET("/api/remember", controllers.RememberController)
15 | route.GET("/api/say", controllers.SayController)
16 |
17 | err := route.Run()
18 | if err != nil {
19 | return
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/backend/internal/common/json_logger_filter.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/gin-gonic/gin"
6 | )
7 |
8 | func JsonLogger() gin.HandlerFunc {
9 | return gin.LoggerWithFormatter(
10 | func(params gin.LogFormatterParams) string {
11 | log := make(map[string]interface{})
12 |
13 | log["status_code"] = params.StatusCode
14 | log["path"] = params.Path
15 | log["method"] = params.Method
16 | log["start_time"] = params.TimeStamp.Format("2023/01/02 - 13:04:05")
17 | log["remote_addr"] = params.ClientIP
18 | log["response_time"] = params.Latency.String()
19 |
20 | str, _ := json.Marshal(log)
21 | return string(str) + "\n"
22 | },
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/backend/internal/services/db_service.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import "os"
4 |
5 | func GetDBCredentials() (string, string) {
6 | dbType := os.Getenv("DB_TYPE")
7 | dbName := os.Getenv("DB_NAME")
8 | dbUser := os.Getenv("DB_USER")
9 | dbPasswd := os.Getenv("DB_PASSWD")
10 | dbHost := os.Getenv("DB_HOST")
11 | dbPort := os.Getenv("DB_PORT")
12 | return dbType, dbUser + ":" + dbPasswd + "@tcp(" + dbHost + ":" + dbPort + ")/" + dbName
13 | }
14 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | # этап сборки (build stage)
2 | FROM node:lts-alpine as build-stage
3 | WORKDIR /app
4 | COPY frontend/package*.json ./
5 | RUN npm install
6 | COPY frontend/. .
7 | RUN npm run build
8 |
9 | # этап production (production-stage)
10 | FROM nginx:stable-alpine as production-stage
11 | COPY --from=build-stage /app/dist /usr/share/nginx/html
12 | EXPOSE 80
13 | CMD ["nginx", "-g", "daemon off;"]
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/README.md:
--------------------------------------------------------------------------------
1 | # frontend
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Lints and fixes files
19 | ```
20 | npm run lint
21 | ```
22 |
23 | ### Customize configuration
24 | See [Configuration Reference](https://cli.vuejs.org/config/).
25 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "baseUrl": "./",
6 | "moduleResolution": "node",
7 | "paths": {
8 | "@/*": [
9 | "src/*"
10 | ]
11 | },
12 | "lib": [
13 | "esnext",
14 | "dom",
15 | "dom.iterable",
16 | "scripthost"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "axios": "^1.6.0",
12 | "bootstrap": "^5.3.1",
13 | "core-js": "^3.8.3",
14 | "vue": "^3.2.13"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.12.16",
18 | "@babel/eslint-parser": "^7.12.16",
19 | "@vue/cli-plugin-babel": "~5.0.0",
20 | "@vue/cli-plugin-eslint": "~5.0.0",
21 | "@vue/cli-service": "~5.0.0",
22 | "eslint": "^7.32.0",
23 | "eslint-plugin-vue": "^8.0.3"
24 | },
25 | "eslintConfig": {
26 | "root": true,
27 | "env": {
28 | "node": true
29 | },
30 | "extends": [
31 | "plugin:vue/vue3-essential",
32 | "eslint:recommended"
33 | ],
34 | "parserOptions": {
35 | "parser": "@babel/eslint-parser"
36 | },
37 | "rules": {}
38 | },
39 | "browserslist": [
40 | "> 1%",
41 | "last 2 versions",
42 | "not dead",
43 | "not ie 11"
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2024/01-werf-deckhouse-gitlab/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
16 |
19 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2024/01-werf-deckhouse-gitlab/frontend/src/assets/logo.png
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 |
4 | createApp(App).mount('#app')
5 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/frontend/vue.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('@vue/cli-service')
2 | module.exports = defineConfig({
3 | transpileDependencies: true
4 | })
5 |
--------------------------------------------------------------------------------
/2024/01-werf-deckhouse-gitlab/werf.yaml:
--------------------------------------------------------------------------------
1 | project: app
2 | configVersion: 1
3 |
4 | ---
5 | image: backend
6 | dockerfile: backend/Dockerfile
7 |
8 | ---
9 | image: frontend
10 | dockerfile: frontend/Dockerfile
11 |
--------------------------------------------------------------------------------
/2024/03-github-actions/.github/workflows/production_deployment.yml:
--------------------------------------------------------------------------------
1 | name: Production Deployment
2 | on:
3 | push:
4 | branches: [main]
5 | jobs:
6 |
7 | converge:
8 | name: Converge
9 | runs-on: ubuntu-latest
10 | steps:
11 |
12 | - name: Checkout code
13 | uses: actions/checkout@v3
14 | with:
15 | fetch-depth: 0
16 |
17 | - name: Converge
18 | uses: werf/actions/converge@v1.2
19 | with:
20 | env: production
21 | kube-config-base64-data: ${{ secrets.KUBE_CONFIG_BASE64_DATA }}
22 | env:
23 | WERF_SET_ENV_URL: "envUrl=http://habrapp.example.com"
24 |
25 |
--------------------------------------------------------------------------------
/2024/03-github-actions/.gitignore:
--------------------------------------------------------------------------------
1 | # ---> Go
2 | # If you prefer the allow list template instead of the deny list, see community template:
3 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
4 | #
5 | # Binaries for programs and plugins
6 | *.exe
7 | *.exe~
8 | *.dll
9 | *.so
10 | *.dylib
11 |
12 | # Test binary, built with `go test -c`
13 | *.test
14 |
15 | # Output of the go coverage tool, specifically when used with LiteIDE
16 | *.out
17 |
18 | # Dependency directories (remove the comment below to include it)
19 | # vendor/
20 |
21 | # Go workspace file
22 | go.work
23 |
24 | # IntellijIdea files
25 | *.iml
26 | .idea
27 | out
28 |
29 | *.DS_Store
30 | ._*
31 |
32 | mysql
--------------------------------------------------------------------------------
/2024/03-github-actions/.helm/templates/deployment-backend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: habr-app-backend
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: habr-app-backend
10 | template:
11 | metadata:
12 | labels:
13 | app: habr-app-backend
14 | spec:
15 | imagePullSecrets:
16 | - name: registrysecret
17 | containers:
18 | - name: app-backend
19 | image: {{ .Values.werf.image.backend }}
20 | ports:
21 | - containerPort: 8080
22 | env:
23 | - name: GIN_MODE
24 | value: "release"
25 | - name: DB_TYPE
26 | value: "mysql"
27 | - name: DB_NAME
28 | value: "habr-app"
29 | - name: DB_USER
30 | value: "root"
31 | - name: DB_PASSWD
32 | value: "password"
33 | - name: DB_HOST
34 | value: "mysql"
35 | - name: DB_PORT
36 | value: "3306"
37 |
--------------------------------------------------------------------------------
/2024/03-github-actions/.helm/templates/deployment-frontend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: habr-app-frontend
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: habr-app-frontend
10 | template:
11 | metadata:
12 | labels:
13 | app: habr-app-frontend
14 | spec:
15 | imagePullSecrets:
16 | - name: registrysecret
17 | containers:
18 | - name: app-frontend
19 | image: {{ .Values.werf.image.frontend }}
20 | ports:
21 | - containerPort: 80
22 | ---
23 | apiVersion: v1
24 | kind: Service
25 | metadata:
26 | name: habr-app-frontend
27 | spec:
28 | selector:
29 | app: habr-app-frontend
30 | ports:
31 | - name: http
32 | port: 80
33 |
--------------------------------------------------------------------------------
/2024/03-github-actions/.helm/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | annotations:
5 | kubernetes.io/ingress.class: nginx
6 | name: habr-app
7 | spec:
8 | rules:
9 | - host: habrapp.zhbert.ru
10 | http:
11 | paths:
12 | - path: /
13 | pathType: Prefix
14 | backend:
15 | service:
16 | name: habr-app-frontend
17 | port:
18 | number: 80
19 | - path: /api
20 | pathType: Prefix
21 | backend:
22 | service:
23 | name: habr-app-backend
24 | port:
25 | number: 8080
26 | tls:
27 | - hosts:
28 | - habrapp.zhbert.ru
29 | secretName: habr-app-tls
30 | ---
31 | apiVersion: cert-manager.io/v1
32 | kind: Certificate
33 | metadata:
34 | name: habr-app
35 | namespace: habr-app
36 | spec:
37 | secretName: habr-app-tls
38 | issuerRef:
39 | kind: ClusterIssuer
40 | name: letsencrypt
41 | dnsNames:
42 | - habrapp.zhbert.ru
43 |
--------------------------------------------------------------------------------
/2024/03-github-actions/.helm/templates/job-db-setup-and-migrate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | # Версия Helm-релиза в имени Job заставит Job каждый раз пересоздаваться.
5 | # Так мы сможем обойти то, что Job неизменяема.
6 | name: "setup-and-migrate-db-rev{{ .Release.Revision }}"
7 | spec:
8 | backoffLimit: 0
9 | template:
10 | spec:
11 | restartPolicy: Never
12 | initContainers:
13 | - name: waiting-mysql
14 | image: alpine:3.19
15 | command: [ '/bin/sh', '-c', 'while ! nc -z mysql 3306; do sleep 1; done' ]
16 | containers:
17 | - name: setup-and-migrate-db
18 | image: {{ .Values.werf.image.backend }}
19 | command: ["/migrations/migrate", "-database", "mysql://root:password@tcp(mysql:3306)/habr-app", "-path", "/migrations/schemes", "up"]
20 |
--------------------------------------------------------------------------------
/2024/03-github-actions/.helm/templates/service-backend.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: habr-app-backend
5 | spec:
6 | selector:
7 | app: habr-app-backend
8 | ports:
9 | - name: http
10 | port: 8080
11 |
--------------------------------------------------------------------------------
/2024/03-github-actions/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Konstantin Nezhbert
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/2024/03-github-actions/README.md:
--------------------------------------------------------------------------------
1 | # habr-action-tests
2 | Test project for an article about GitHub Actions
3 |
--------------------------------------------------------------------------------
/2024/03-github-actions/backend/Dockerfile:
--------------------------------------------------------------------------------
1 | # Используем многоступенчатую сборку образа (multi-stage build)
2 | # Образ, в котором будет собираться проект
3 | FROM golang:1.18-alpine AS build
4 | # Устанавливаем curl и tar.
5 | RUN apk add curl tar
6 | # Копируем исходники приложения
7 | COPY backend/. /app
8 | WORKDIR /app
9 | # Скачиваем утилиту migrate и распаковываем полученный архив.
10 | RUN curl -L https://github.com/golang-migrate/migrate/releases/download/v4.16.2/migrate.linux-amd64.tar.gz | tar xvz
11 | # Запускаем загрузку нужных пакетов.
12 | RUN go mod download
13 | # Запускаем сборку приложения.
14 | RUN go build -o /goapp cmd/main.go
15 |
16 | # Образ, который будет разворачиваться в кластере.
17 | FROM alpine:latest
18 | WORKDIR /
19 | # Копируем из сборочного образа исполняемый файл проекта.
20 | COPY --from=build /goapp /goapp
21 | # Копируем из сборочного образа распакованный файл утилиты migrate и схемы миграции.
22 | COPY --from=build /app/migrate /migrations/migrate
23 | COPY backend/db/migrations /migrations/schemes
24 | EXPOSE 8080
25 | ENTRYPOINT ["/goapp"]
26 |
--------------------------------------------------------------------------------
/2024/03-github-actions/backend/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "habr_app/internal/app"
4 |
5 | func main() {
6 | app.Run()
7 | }
8 |
--------------------------------------------------------------------------------
/2024/03-github-actions/backend/db/migrations/000001_create_talkers_table.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS talkers;
--------------------------------------------------------------------------------
/2024/03-github-actions/backend/db/migrations/000001_create_talkers_table.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE talkers (
2 | id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
3 | message TEXT NOT NULL,
4 | name TEXT NOT NULL
5 | );
--------------------------------------------------------------------------------
/2024/03-github-actions/backend/internal/app/app.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "habr_app/internal/common"
6 | "habr_app/internal/controllers"
7 | )
8 |
9 | func Run() {
10 | route := gin.New()
11 | route.Use(gin.Recovery())
12 | route.Use(common.JsonLogger())
13 |
14 | route.GET("/api/remember", controllers.RememberController)
15 | route.GET("/api/say", controllers.SayController)
16 |
17 | err := route.Run()
18 | if err != nil {
19 | return
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/2024/03-github-actions/backend/internal/common/json_logger_filter.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/gin-gonic/gin"
6 | )
7 |
8 | func JsonLogger() gin.HandlerFunc {
9 | return gin.LoggerWithFormatter(
10 | func(params gin.LogFormatterParams) string {
11 | log := make(map[string]interface{})
12 |
13 | log["status_code"] = params.StatusCode
14 | log["path"] = params.Path
15 | log["method"] = params.Method
16 | log["start_time"] = params.TimeStamp.Format("2023/01/02 - 13:04:05")
17 | log["remote_addr"] = params.ClientIP
18 | log["response_time"] = params.Latency.String()
19 |
20 | str, _ := json.Marshal(log)
21 | return string(str) + "\n"
22 | },
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/2024/03-github-actions/backend/internal/services/db_service.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import "os"
4 |
5 | func GetDBCredentials() (string, string) {
6 | dbType := os.Getenv("DB_TYPE")
7 | dbName := os.Getenv("DB_NAME")
8 | dbUser := os.Getenv("DB_USER")
9 | dbPasswd := os.Getenv("DB_PASSWD")
10 | dbHost := os.Getenv("DB_HOST")
11 | dbPort := os.Getenv("DB_PORT")
12 | return dbType, dbUser + ":" + dbPasswd + "@tcp(" + dbHost + ":" + dbPort + ")/" + dbName
13 | }
14 |
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | # этап сборки (build stage)
2 | FROM node:lts-alpine as build-stage
3 | WORKDIR /app
4 | COPY frontend/package*.json ./
5 | RUN npm install
6 | COPY frontend/. .
7 | RUN npm run build
8 |
9 | # этап production (production-stage)
10 | FROM nginx:stable-alpine as production-stage
11 | COPY --from=build-stage /app/dist /usr/share/nginx/html
12 | EXPOSE 80
13 | CMD ["nginx", "-g", "daemon off;"]
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/README.md:
--------------------------------------------------------------------------------
1 | # frontend
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Lints and fixes files
19 | ```
20 | npm run lint
21 | ```
22 |
23 | ### Customize configuration
24 | See [Configuration Reference](https://cli.vuejs.org/config/).
25 |
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "baseUrl": "./",
6 | "moduleResolution": "node",
7 | "paths": {
8 | "@/*": [
9 | "src/*"
10 | ]
11 | },
12 | "lib": [
13 | "esnext",
14 | "dom",
15 | "dom.iterable",
16 | "scripthost"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "axios": "^1.4.0",
12 | "bootstrap": "^5.3.1",
13 | "core-js": "^3.8.3",
14 | "vue": "^3.2.13"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.12.16",
18 | "@babel/eslint-parser": "^7.12.16",
19 | "@vue/cli-plugin-babel": "~5.0.0",
20 | "@vue/cli-plugin-eslint": "~5.0.0",
21 | "@vue/cli-service": "~5.0.0",
22 | "eslint": "^7.32.0",
23 | "eslint-plugin-vue": "^8.0.3"
24 | },
25 | "eslintConfig": {
26 | "root": true,
27 | "env": {
28 | "node": true
29 | },
30 | "extends": [
31 | "plugin:vue/vue3-essential",
32 | "eslint:recommended"
33 | ],
34 | "parserOptions": {
35 | "parser": "@babel/eslint-parser"
36 | },
37 | "rules": {}
38 | },
39 | "browserslist": [
40 | "> 1%",
41 | "last 2 versions",
42 | "not dead",
43 | "not ie 11"
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2024/03-github-actions/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
16 |
19 |
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flant/examples/f8252a9f4cffff96df585ee6faa5cfed50bfdd63/2024/03-github-actions/frontend/src/assets/logo.png
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 |
4 | createApp(App).mount('#app')
5 |
--------------------------------------------------------------------------------
/2024/03-github-actions/frontend/vue.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('@vue/cli-service')
2 | module.exports = defineConfig({
3 | transpileDependencies: true
4 | })
5 |
--------------------------------------------------------------------------------
/2024/03-github-actions/werf.yaml:
--------------------------------------------------------------------------------
1 | project: habr-app
2 | configVersion: 1
3 |
4 | ---
5 | image: backend
6 | dockerfile: backend/Dockerfile
7 |
8 | ---
9 | image: frontend
10 | dockerfile: frontend/Dockerfile
11 |
--------------------------------------------------------------------------------