├── 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 | 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 | 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 | 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 | 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 | --------------------------------------------------------------------------------