diff --git a/.github/workflows/apply-dev.yml b/.github/workflows/apply-dev.yml new file mode 100644 index 0000000..6a40ec2 --- /dev/null +++ b/.github/workflows/apply-dev.yml @@ -0,0 +1,45 @@ +name: Apply terraform resources dev + +on: [workflow_dispatch] + +env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + +jobs: + apply-dev: + name: 'terraform apply dev' + runs-on: ubuntu-latest + env: + TF_WORKING_DIR: 'terraform' + + defaults: + run: + shell: bash + working-directory: ${{ env.TF_WORKING_DIR }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v1 + with: + terraform_version: 0.15.5 + + - name: Terraform fmt + id: fmt + run: terraform fmt + + - name: Terraform Init + id: init + run: terraform init + + - name: Terraform Validate + id: validate + run: terraform validate -no-color + + - name: Terraform Apply + run: terraform apply -auto-approve + + diff --git a/.github/workflows/destroy-apply.yml b/.github/workflows/destroy-apply.yml new file mode 100644 index 0000000..7896722 --- /dev/null +++ b/.github/workflows/destroy-apply.yml @@ -0,0 +1,45 @@ +name: Destroy terraform resources on the given folder + +on: + workflow_dispatch: + inputs: + dir: + description: 'Directory with resources to be destroyed' + required: true + default: 'terraform' + +env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + +jobs: + destroy-dev: + name: 'terraform destroy dev' + runs-on: ubuntu-latest + env: + TF_WORKING_DIR: ${{ github.event.inputs.dir }} + + defaults: + run: + shell: bash + working-directory: ${{ env.TF_WORKING_DIR }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v1 + with: + terraform_version: 0.15.5 + + - name: Terraform Init + id: init + run: terraform init + + - name: Terraform Validate + id: validate + run: terraform validate -no-color + + - name: Terraform Destroy + run: terraform destroy -auto-approve \ No newline at end of file diff --git a/.github/workflows/push-registry-ghcr.yml b/.github/workflows/push-registry-ghcr.yml new file mode 100644 index 0000000..bcb3633 --- /dev/null +++ b/.github/workflows/push-registry-ghcr.yml @@ -0,0 +1,53 @@ +# name: Docker Image GHCR + +# on: +# workflow_dispatch + +# jobs: +# build_and_publish: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v3 +# - name: GitHub Docker registry +# uses: matootie/github-docker@v3.1.0 +# with: +# accessToken: ${{secrets.GH_PAT}} +# tag: latest +# tag_with_ref: true +# tag_with_sha: true + +# # Dockerfile 2 + +name: build-and-publish-container-image + +on: [workflow_dispatch] + +jobs: + build-and-publish-container-image: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Build and tag Docker image + run: | + short_commit=$(git rev-parse --short HEAD) + cd projeto-fsharp + docker build . -t ghcr.io/richardneves/projeto-fsharp:latest -t ghcr.io/richardneves/projeto-fsharp:${short_commit} + + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GH_PAT }} + + - name: Push Docker image + run: | + short_commit=$(git rev-parse --short HEAD) + docker push ghcr.io/richardneves/projeto-fsharp:${short_commit} + docker push ghcr.io/richardneves/projeto-fsharp:latest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f57af93 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# Local .terraform directories +**/.terraform/* +/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log + +# Ignore any .tfvars files that are generated automatically for each Terraform run. Most +# .tfvars files are managed as part of configuration and so should be included in +# version control. +# +# example.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +.idea +*.zip +node_modules +.terraform +.DS_STORE \ No newline at end of file diff --git a/README.md b/README.md index 9e2060e..c6e57f7 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,24 @@ # Datapi DevOps Challenge -Para melhor entendermos o seu nível técnico, nós preparamos este desafio como parte do nosso processo de contratação. Por isso, tenha em mente que não é necessário cumprir com todos os pontos mencionados, nem cumpri-los em uma ordem específica. +Documentação do projeto -O importante é entregar o que você conseguir fazer, com a devida documentação. +# Instanciação de Maquina Virtual EC2 AWS: -# Desafios +Foi escolhida a cloud AWS para realização do projeto e Deploy por questão de familiaridade. Porém não afeta no funcionamento do projeto. -Segue abaixo uma lista de desafios abrangendo várias áreas de responsabilidade para um DevOps no time do Datapi. Nossa sugestão é tentar seguir cada item na ordem apresentada, porém você está livre para atuar nos pontos que quiser e tiver mais familiaridade. +Onde é utilizado pelo arquivo apply-dev.yml para deploy na AWS. Utilizando Steps e jobs basicos para entrega da maquina EC2 na AWS -- Instanciar uma VM numa cloud provider. Recomendação: Microsoft Azure. - - Criar a configuração dessa VM usando uma ferramenta de IaC (Infrastructure as Code). Recomendação: Terraform. - - Criar um job de CI (Continuous Integration) para aplicar a configuração da ferramenta de provisionamento. Recomendação: GitHub Actions. -- Adicionar um Dockerfile à aplicação disponibilizada na pasta `projeto-fsharp/` para containerizar o mesmo. Note que foi utilizada a linguagem F# (.NET) para escrever a aplicação. Para facilitar o entendimento do projeto, adicionamos um README.md com instruções de teste e uso do mesmo localmente. Você deverá ser capaz de traduzir essas instruções para a criação do Dockerfile. - - Criar um job de CI para enviar a imagem gerada para um Docker Registry. Recomendação: GitHub Container Registry. -- Criar os manifestos YAML para hospedar a aplicação usando Kubernetes. Nesse ponto os testes podem ser realizados apenas localmente, porém devem ser apresentados os arquivos YAML criados. - * Utilizar IaC para configurar o Kubernetes. Recomendação: Terraform. - * Configurar a hospedagem a partir do registry gerado na tarefa anterior. - * Caso possua mais familiaridade, sinta-se motivado a customizar mais as configurações (secrets, ingress, etc.). -- Adicionar um README ao projeto detalhando o processo e justificando as decisões tomadas. Recomendação: Markdown. Todos os refinamentos adicionados nos tópicos mencionados anteriormente, e demais ideias que possam melhorar o projeto serão considerados na avaliação da solução. +# Dockerfile: -Faça um fork e envie um PR com a sua solução, o tempo de entrega é de no máximo 4 dias e será contabilizado a partir da data do fork. +Foi realizado a construção do Docker file MultiStage onde em primeiro momento é possível baixar a imagem da Microsoft com a versão 6.0 do SDK para o bom teste e funcionamento do projeto, realizar copia dos arquivos e pastas principais para aplicação do restore.sh e criação do build. +Na segunda etapa de build é possível realizar a copia do build e armazenar em uma pasta especifica onde será utiliza o resultado do build a exposição da porta e utilização do arquivo ./Server em ENTRYPOINT inicialização em tempo de execução do container. -## Será avaliado: +O DockerFile é utilizado pelo arquivo de esteira na pasta .github/workflows push-registry-ghcr.yml onde existe um job basico de conexão, build e push da imagem para o registry do github onde é possível localizar em packages do repositorio. -- % do que foi entregue em relação ao que foi pedido. -- Qualidade dos aquivos Terraform. -- Boas práticas de infra e uso do Kubernetes. -- Corretude das tarefas. -- Uso eficiente em relação ao custo de máquina. +# Arquivos Terraform + +Arquivos básicos onde fazem a construção da EC2 na AWS e em paralelo também fazem a criação do K3S dentro da EC2 da AWS que é uma opção escolhida ao kubernetes pela praticidade em sua configuração. E é realizado o deploy da aplicação via manifesto com shell script. + +# Destroy terraform + +Existe o arquivo terraform destroy-apply.yml onde é executado manualmente o "destroy" da aplicação criada pelo terraform. \ No newline at end of file diff --git a/projeto-fsharp/Dockerfile b/projeto-fsharp/Dockerfile new file mode 100644 index 0000000..9653b2c --- /dev/null +++ b/projeto-fsharp/Dockerfile @@ -0,0 +1,17 @@ +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /app + +COPY . projeto-fsharp +RUN cd projeto-fsharp && ./restore.sh + +RUN cd projeto-fsharp && dotnet fake run build.fsx -t "Build" + +FROM mcr.microsoft.com/dotnet/sdk:6.0 +LABEL org.opencontainers.image.source="https://github.com/richardneves/Datapi-DevOps-Challenge" +WORKDIR /app + +COPY --from=build /app/projeto-fsharp/src/Server/out . + +EXPOSE 8085 + +ENTRYPOINT ["./Server"] \ No newline at end of file diff --git a/projeto-fsharp/restore.sh b/projeto-fsharp/restore.sh index a944206..9703ace 100755 --- a/projeto-fsharp/restore.sh +++ b/projeto-fsharp/restore.sh @@ -1,5 +1,4 @@ set -ex - dotnet tool restore dotnet paket install dotnet paket restore diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..09aa56f --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -0,0 +1,63 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.25.0" + constraints = ">= 3.44.0" + hashes = [ + "h1:u1MptxxzcfkA4N0AXGdkyKzjRZK+ssnNmldl5kCNwSM=", + "zh:163244f73f13b013d9a6a267731df5971d03a3cdcdefb6d280dcbb39e482d76b", + "zh:179debe1fcfff552589c949e068ff7cae5f4be6b00b48f20933029795210f4c1", + "zh:1c66da7cd54614c57d3f7fa0f5f2b51864358250d360cdb6c403f4248ab15fd4", + "zh:360cdc430c4e79a7b9b791832460e9caccafd9dd1d9bb9a95293aaebce5d506c", + "zh:40eb5dd1e678528fb881b448624c68f10f1878bfbbf4160c829bb2255f8ae159", + "zh:76a30a8a5cc9132202929c4ec5dd4f1ac4089af73a0d417a965cd23801e69526", + "zh:7fbd7796e787635640ba5b489b6b1cd9294156e7f222f70f0b9672d0302bbf67", + "zh:8408832a7540e758397e55dba2134b7428b7318d515f7226dd548231bc61e3df", + "zh:97db8d60e240701b7010eff29e3d44360856b7f3e72a217d1a57d6315bbb8e08", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9f9c7b9203ac445b65ca8f3cfa176d9e86dc5572ce925490d7fabb04ebf4f829", + "zh:ca5c18799ab858a1addcbfb10cf227e013eb12c110b7d750c969fc8711ff578b", + "zh:cdd503da9f461ad5bc119cb3003be85b3752457b552f41ed20315e1239e3084a", + "zh:f8aaad1544823803dfd03e22ed260e706524baf5509cb1c4cf2be9d982c0fa6c", + "zh:ff283a7aaf509b3a9ba17a2efbc2faccbc69d84e913b23f2434dd5102becac3c", + ] +} + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.23.0" + hashes = [ + "h1:xyFc77aYkPoU4Xt1i5t0B1IaS8TbTtp9aCSuQKDayII=", + "zh:10488a12525ed674359585f83e3ee5e74818b5c98e033798351678b21b2f7d89", + "zh:1102ba5ca1a595f880e67102bbf999cc8b60203272a078a5b1e896d173f3f34b", + "zh:1347cf958ed3f3f80b3c7b3e23ddda3d6c6573a81847a8ee92b7df231c238bf6", + "zh:2cb18e9f5156bc1b1ee6bc580a709f7c2737d142722948f4a6c3c8efe757fa8d", + "zh:5506aa6f28dcca2a265ccf8e34478b5ec2cb43b867fe6d93b0158f01590fdadd", + "zh:6217a20686b631b1dcb448ee4bc795747ebc61b56fbe97a1ad51f375ebb0d996", + "zh:8accf916c00579c22806cb771e8909b349ffb7eb29d9c5468d0a3f3166c7a84a", + "zh:9379b0b54a0fa030b19c7b9356708ec8489e194c3b5e978df2d31368563308e5", + "zh:aa99c580890691036c2931841e88e7ee80d59ae52289c8c2c28ea0ac23e31520", + "zh:c57376d169875990ac68664d227fb69cd0037b92d0eba6921d757c3fd1879080", + "zh:e6068e3f94f6943b5586557b73f109debe19d1a75ca9273a681d22d1ce066579", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/hashicorp/null" { + version = "3.2.1" + hashes = [ + "h1:FbGfc+muBsC17Ohy5g806iuI1hQc4SIexpYCrQHQd8w=", + "zh:58ed64389620cc7b82f01332e27723856422820cfd302e304b5f6c3436fb9840", + "zh:62a5cc82c3b2ddef7ef3a6f2fedb7b9b3deff4ab7b414938b08e51d6e8be87cb", + "zh:63cff4de03af983175a7e37e52d4bd89d990be256b16b5c7f919aff5ad485aa5", + "zh:74cb22c6700e48486b7cabefa10b33b801dfcab56f1a6ac9b6624531f3d36ea3", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:79e553aff77f1cfa9012a2218b8238dd672ea5e1b2924775ac9ac24d2a75c238", + "zh:a1e06ddda0b5ac48f7e7c7d59e1ab5a4073bbcf876c73c0299e4610ed53859dc", + "zh:c37a97090f1a82222925d45d84483b2aa702ef7ab66532af6cbcfb567818b970", + "zh:e4453fbebf90c53ca3323a92e7ca0f9961427d2f0ce0d2b65523cc04d5d999c2", + "zh:e80a746921946d8b6761e77305b752ad188da60688cfd2059322875d363be5f5", + "zh:fbdb892d9822ed0e4cb60f2fedbdbb556e4da0d88d3b942ae963ed6ff091e48f", + "zh:fca01a623d90d0cad0843102f9b8b9fe0d3ff8244593bd817f126582b52dd694", + ] +} diff --git a/terraform/backend.tf b/terraform/backend.tf new file mode 100644 index 0000000..93775cd --- /dev/null +++ b/terraform/backend.tf @@ -0,0 +1,8 @@ +terraform { + backend "s3" { + bucket = "richardneves-tfstate" + key = "dev/tfstate-challenge" + region = "us-east-1" + encrypt = true + } +} \ No newline at end of file diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..35fc209 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,17 @@ +data "aws_ami" "ubuntu" { + owners = ["amazon"] + most_recent = true + name_regex = "Ubuntu" +} + +resource "aws_instance" "web" { + ami = data.aws_ami.ubuntu.id + instance_type = var.instance_type + security_groups = ["acesso_geral"] + user_data = file("terraformando.sh") + tags = { + Name = "${var.environment}: App" + Env = var.environment + Type = var.instance_type + } +} \ No newline at end of file diff --git a/terraform/sg.tf b/terraform/sg.tf new file mode 100644 index 0000000..ab9a255 --- /dev/null +++ b/terraform/sg.tf @@ -0,0 +1,75 @@ +resource "aws_security_group" "acesso_geral" { + name = "acesso_geral" + description = "grupo de dev" + + ingress { + cidr_blocks = ["0.0.0.0/0"] + ipv6_cidr_blocks = ["::/0"] + from_port = 22 + to_port = 22 + protocol = "tcp" + } + + egress { + cidr_blocks = ["0.0.0.0/0"] + ipv6_cidr_blocks = ["::/0"] + from_port = 22 + to_port = 22 + protocol = "tcp" + } + + ingress { + cidr_blocks = ["0.0.0.0/0"] + ipv6_cidr_blocks = ["::/0"] + from_port = 8080 + to_port = 8080 + protocol = "tcp" + } + + egress { + cidr_blocks = ["0.0.0.0/0"] + ipv6_cidr_blocks = ["::/0"] + from_port = 8080 + to_port = 8080 + protocol = "tcp" + } + + + ingress { + cidr_blocks = ["0.0.0.0/0"] + ipv6_cidr_blocks = ["::/0"] + from_port = 443 + to_port = 443 + protocol = "tcp" + } + + egress { + cidr_blocks = ["0.0.0.0/0"] + ipv6_cidr_blocks = ["::/0"] + from_port = 443 + to_port = 443 + protocol = "tcp" + } + + + ingress { + cidr_blocks = ["0.0.0.0/0"] + ipv6_cidr_blocks = ["::/0"] + from_port = 80 + to_port = 80 + protocol = "tcp" + } + + egress { + cidr_blocks = ["0.0.0.0/0"] + ipv6_cidr_blocks = ["::/0"] + from_port = 80 + to_port = 80 + protocol = "tcp" + } + + tags = { + Name = "my-app" + } + +} diff --git a/terraform/terraformando.sh b/terraform/terraformando.sh new file mode 100644 index 0000000..bdfc102 --- /dev/null +++ b/terraform/terraformando.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +sudo apt-get update -y + +sudo apt-get upgrade -y + +sudo curl -sfL https://get.k3s.io | sh - + +sudo chmod 644 /etc/rancher/k3s/k3s.yaml + +sudo export KUBECONFIG=/etc/rancher/k3s/k3s.yaml + +export KUBECONFIG=/etc/rancher/k3s/k3s.yaml +kubectl get pods --all-namespaces +helm ls --all-namespaces + +cat << eof > deployment.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: projeto-fsharp + +--- +apiVersion: v1 +kind: Service +metadata: + name: projeto-fsharp + namespace: projeto-fsharp +spec: + type: NodePort + ports: + - port: 8085 + targetPort: 8085 + nodePort: 30085 + selector: + app: projeto-fsharp + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: projeto-fsharp + namespace: projeto-fsharp +spec: + replicas: 1 + selector: + matchLabels: + app: projeto-fsharp + template: + metadata: + labels: + app: projeto-fsharp + spec: + containers: + - name: projeto-fsharp + image: ghcr.io/richardneves/datapi-devops-challenge/datapi-devops-challenge:latest + ports: + - containerPort: 8085 + resources: + requests: + memory: "256Mi" + cpu: "0.25" + limits: + memory: "512Mi" + cpu: "0.5" + imagePullPolicy: Always +eof + +k3s kubectl apply -f deployment.yaml \ No newline at end of file diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..6326825 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,25 @@ +variable "aws_region" { + type = string + description = "The AWS region to deploy to" + default = "us-east-1" +} + +variable "aws_profile" { + type = string + description = "The AWS profile to use to execute the commands" + default = "default" + +} + +variable "environment" { + type = string + description = "The environment to deploy to" + default = "dev" + +} + +variable "instance_type" { + type = string + description = "The instance power" + default = "t2.micro" +} diff --git a/terraform/version.tf b/terraform/version.tf new file mode 100644 index 0000000..9d90598 --- /dev/null +++ b/terraform/version.tf @@ -0,0 +1,22 @@ +provider "aws" { + region = var.aws_region + + default_tags { + tags = { + Project = "Terraform GHA setup" + CreatedAt = "2021-06-05" + ManagedBy = "Terraform" + Owner = "Web enterprise" + } + } +} + + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.44.0" + } + } +}