commit cd8dd67e5b96a79f4e5798384e2080d3b038fb51 Author: Maxim Samoilov Date: Tue Jul 7 12:12:13 2020 +0300 Initial commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cca359a --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ + +BRANCH ?= master + +build-app: + docker build app --tag quay.csssr.cloud/csssr/test-app:$(BRANCH) + docker push quay.csssr.cloud/csssr/test-app:$(BRANCH) + +HELM ?= helm3 + +deploy: + $(HELM) upgrade --install my-app-$(BRANCH) chart --set image.tag=$(BRANCH) --set ingress.host=$(BRANCH).my-app.com diff --git a/README.md b/README.md new file mode 100644 index 0000000..8485803 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Приложение для загрузки и получения картинки + +## Функциональность + +Загрузка картинки + +```sh +curl -F 'image=@/path/to/image.png' my-app.com/upload +``` + +Получение картинки + +```sh +curl http://my-app.com/image -o image.jpg +``` + +При загрузки следующей картинки предыдущая удаляется + +## Требования + +- Zero-downtime deployment +- 100% uptime +- Возможность делать несколько релизов для разных веток в один неймспейс (для тестирования) + +## Деплой + +Сборка + +```sh +make build BRANCH=master +``` + +Деплой helm чарта (используется helm 3) + +```sh +make deploy HELM=helm +``` diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..2706aa8 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +tmp-image diff --git a/app/Dockerfile b/app/Dockerfile new file mode 100644 index 0000000..7752b02 --- /dev/null +++ b/app/Dockerfile @@ -0,0 +1,9 @@ +FROM golang:1.14 + +WORKDIR /app + +COPY . . + +RUN go build -o app + +CMD /app/app diff --git a/app/go.mod b/app/go.mod new file mode 100644 index 0000000..d7d5384 --- /dev/null +++ b/app/go.mod @@ -0,0 +1,5 @@ +module github.com/CSSSR/my-app + +go 1.14 + +require github.com/logrusorgru/aurora v2.0.3+incompatible diff --git a/app/go.sum b/app/go.sum new file mode 100644 index 0000000..463110f --- /dev/null +++ b/app/go.sum @@ -0,0 +1,2 @@ +github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= +github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= diff --git a/app/main.go b/app/main.go new file mode 100644 index 0000000..0636883 --- /dev/null +++ b/app/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + + "github.com/logrusorgru/aurora" +) + +type appConfig struct { + Port string + ImagePath string +} + +var config appConfig + +func init() { + config.Port = os.Getenv("PORT") + if config.Port == "" { + config.Port = "3000" + } + + config.ImagePath = os.Getenv("IMAGE_PATH") + if config.ImagePath == "" { + config.ImagePath = "tmp-image" + } +} + +func uploadFile(w http.ResponseWriter, r *http.Request) { + file, _, err := r.FormFile("image") + if err != nil { + log.Fatalf("Error Retrieving the File %v", err) + } + defer file.Close() + + fileBytes, err := ioutil.ReadAll(file) + if err != nil { + log.Fatalf("Error reading file %v", err) + } + + ioutil.WriteFile(config.ImagePath, fileBytes, os.FileMode(0600)) + + log.Println(aurora.Cyan("Successfully Uploaded File")) + fmt.Fprintf(w, "Successfully Uploaded File\n") +} + +func image(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, config.ImagePath) +} + +func livenessProbe(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "ok") +} + +func readinessProbe(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "ok") +} + +func main() { + http.HandleFunc("/upload", uploadFile) + http.HandleFunc("/image", image) + http.HandleFunc("/healthz/liveness", livenessProbe) + http.HandleFunc("/healthz/readiness", readinessProbe) + + err := http.ListenAndServe(fmt.Sprintf(":%s", config.Port), nil) + if err != nil { + log.Fatal(err) + } +} diff --git a/chart/.gitignore b/chart/.gitignore new file mode 100644 index 0000000..ee3892e --- /dev/null +++ b/chart/.gitignore @@ -0,0 +1 @@ +charts/ diff --git a/chart/.helmignore b/chart/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/chart/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 0000000..e96f591 --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,23 @@ +apiVersion: v2 +name: my-project +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +appVersion: 1.16.0 diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml new file mode 100644 index 0000000..aeab116 --- /dev/null +++ b/chart/templates/deployment.yaml @@ -0,0 +1,32 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} +spec: + replicas: 1 + selector: + matchLabels: + app: my-app + template: + metadata: + labels: + app: my-app + spec: + containers: + - name: my-app + image: quay.csssr.cloud/csssr/test-app:{{ .Values.image.tag }} + imagePullPolicy: Always + env: + - name: PORT + value: "80" + - name: IMAGE_PATH + value: /data/image + ports: + - containerPort: 80 + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + persistentVolumeClaim: + claimName: my-app diff --git a/chart/templates/ingress.yaml b/chart/templates/ingress.yaml new file mode 100644 index 0000000..335382d --- /dev/null +++ b/chart/templates/ingress.yaml @@ -0,0 +1,15 @@ +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: {{ .Release.Name }} + annotations: + kubernetes.io/ingress.class: nginx +spec: + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - path: / + backend: + serviceName: my-app + servicePort: 80 diff --git a/chart/templates/pvc.yaml b/chart/templates/pvc.yaml new file mode 100644 index 0000000..1cb9d89 --- /dev/null +++ b/chart/templates/pvc.yaml @@ -0,0 +1,10 @@ +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ .Release.Name }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml new file mode 100644 index 0000000..58ca3d9 --- /dev/null +++ b/chart/templates/service.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} +spec: + ports: + - port: 80 + targetPort: 80 + selector: + app: my-app diff --git a/chart/values.yaml b/chart/values.yaml new file mode 100644 index 0000000..5cce473 --- /dev/null +++ b/chart/values.yaml @@ -0,0 +1,5 @@ +image: + tag: master + +ingress: + host: my-app.com diff --git a/image-example.jpg b/image-example.jpg new file mode 100644 index 0000000..7b13b08 Binary files /dev/null and b/image-example.jpg differ