Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Datarisk DevOps test #3

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5d69372
feat: add Dockerfile
Nov 11, 2023
ee768d9
feat: add github docker workflow
Nov 11, 2023
c82164a
fix: typo 'runs' in job
Nov 11, 2023
7ed6763
fix: add 'master' to the trigger on push
Nov 11, 2023
43d85c7
fix: update login to registry step
Nov 11, 2023
5ddedb3
feat: add workflow for terraform plan
Nov 13, 2023
bcdd30a
feat: run workflow only when changes app or dockerfile
Nov 13, 2023
bbefb8e
fix: fix typo in workflow trigger
Nov 13, 2023
7d60083
fix: paths in docker workflow
Nov 13, 2023
a4fd6a9
fix: trigger on push for tf workflow
Nov 13, 2023
db7ed8d
fix: fix to paths in docker worflow
Nov 13, 2023
722b510
test: trigger docker workflow
Nov 13, 2023
e538a47
feat: add vm terraform
Nov 13, 2023
324e811
fix: add branches to terraform workflow
Nov 13, 2023
ac1a928
fix: fix path to trigger workflow on terraform changes
Nov 13, 2023
82e9258
fix: remove paths from tf worflow
Nov 13, 2023
0861dba
fix: add write permission to id-token in tf worflow
Nov 13, 2023
e31795f
feat: change azure auth to env vars
Nov 13, 2023
446c6a9
fix: set the correct folder for tf workflow
Nov 13, 2023
06619ec
fix: simplify the tf plan step
Nov 13, 2023
e80c52c
fix: remove cd command
Nov 13, 2023
ab40246
feat: add missing vm pub key
Nov 13, 2023
4bda073
feat: add kubernetes yaml files
Nov 13, 2023
b1a3ac8
feat: add azure backend to tfstate
Nov 13, 2023
36b8b44
fix: remove sensible info from secret
Nov 13, 2023
ecc42d8
feat: add challenge explanation doc
Nov 13, 2023
26cd819
fix: update workflow trigger to pull request
Nov 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/publish-ghcr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Docker Build and Publish

on:
push:
branches:
- main
- master
paths:
- 'Dockerfile'
- 'projeto-fsharp/**'

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v3

- name: Build Docker Image
run: |
docker build -t docker-image .

- name: Login to Registry
run: |
docker login ${{ vars.REGISTRY }} \
-u ${{ secrets.REGISTRY_USERNAME }} \
--password ${{ secrets.REGISTRY_PASSWORD }}

- name: Push to Registry
run: |
IMAGE_NAME="${{ vars.REGISTRY }}/${{ secrets.REGISTRY_USERNAME }}/${{ vars.PROJECT_NAME }}"
docker tag docker-image:latest $IMAGE_NAME:latest
docker push $IMAGE_NAME:latest
33 changes: 33 additions & 0 deletions .github/workflows/tf-plan.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Terraform Plan

on:
pull_request:
paths:
- terraform/**

jobs:
plan-dev:
name: 'Terraform plan DEV'
runs-on: 'ubuntu-latest'

env:
ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}

steps:
- name: Checkout Repository
uses: actions/checkout@v3

- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
terraform_version: 1.5.0

- name: Terraform Plan
run: |
pwd
terraform init
terraform plan
working-directory: terraform
35 changes: 35 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Build layer

# Official .NET SDK image as a base image
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build

# Build directory
WORKDIR /build

# Copy the F# API app source code to the container
COPY projeto-fsharp .

# Run the restore script
RUN ./restore.sh

# Run the build command to build the app
RUN dotnet fake run build.fsx -t "Build"

# Run layer

# Official .NET runtime image
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime

# Working app directory
WORKDIR /app

# Copy the built app from the build layer
COPY --from=build /build/src/Server/out /app

# Expose the port that the app will run on
EXPOSE 8085

# Command to run the app when the container starts
CMD ["dotnet", "Server.dll"]

# test
71 changes: 71 additions & 0 deletions descricao.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Máquina Virtual

Na pasta `terraform` estão os arquivos para criação da máquina virtual.

- Os recursos foram nomeados usando um prefixo `datapi`. O nome do workspace também faz parte do nome, o que facilita o uso destes mesmos recursos para diferentes ambientes.
- Um ip público foi adicionado ao recurso de `network interface` para tornar a máquina acessivel externamente.
- Uma chave ssh pública foi adicionada `vm.pub` para permitir o acesso via ssh.
- A VM foi criada com linux, rodando `Ubuntu 22.04`.
- A senha de admin foi gerada de forma aleatória, e é somente exibida durante a aplicação do plano como `output`.
- O arquivo `providers` está configurado para salvar o `tfstate` em uma `storage` na Azure. O importante aqui é criar uma `key` unica para cada projeto, para evitar conflitos entre arquivos terraform.

# Docker

A construção do `Dockerfile` está dividido em duas etapas: `build` e `runtime`.

## Build

- Usa a imagem .NET SDK `6.0.x`
- Copia os arquivos do repositório
- Restaura as dependencias
- Chama o `fake build`

## Runtime

- Usa a imagem .NET Runtime `6.0.x`
- Copia os arquivos gerados pelo build
- Exporta a porta usada pelo app `8085`
- Define o `entrypoint` chamando a aplicação

A divisão dessas etapas ajuda a diminuir o tamanho da imagem final, e também mantém apenas os arquivos necessários para executar a aplicação.

# CI/CD

Foram criados dois workflows:

## publish-ghcr

Realiza o build da imagem Docker da aplicação e faz o envio para o registry do Github.

Requer algumas variaveis e segredos:
- var `REGISTRY`: a URL do registry desejado (`ghcr.io`)
- var `PROJECT_NAME`: com o nome do projeto, que vai ser usado como nome da imagem
- secret `REGISTRY_USERNAME`: o `username` do dono do repositório
- secret `REGISTRY_PASSWORD`: o token pessoal com acesso ao registry do github

A execução desse workflow foi restrito para apenas ocorrer quando houver mudanças nos arquivos da aplicação (pasta `projeto-fsharp`), ou no arquivo `Dockerfile`.

## tf-plan

Executa o `plan` do Terraform para garantir que as mudanças estão corretas.

Requer alguns segredos para autenticar com a Azure:
- secret `ARM_CLIENT_ID`: O ID da aplicação
- secret `ARM_CLIENT_SECRET`: O segredo criado para a aplicação
- secret `ARM_SUBSCRIPTION_ID`: O ID da `subscription` da conta
- secret `ARM_TENANT_ID`: O ID do `tenant` da aplicação

A execução desse workflow foi restrito para ser executado apenas em `pull requests` se arquivos da pasta `terraform` foram modificados.

# Kubernetes

Os arquivos YAML para deploy da aplicação em um cluster Kubernetes.

Todo o deploy foi criado usando um arquivo de `kustomization`, que lista os demais arquivos:
- `namespace.yaml`: cria um namespace para aplicação
- `deployment.yaml`: configura o deployment do app
- `secret.yaml`: cria um segredo com a autenticação com o GHCR.io

O arquivo de `kustomization` também inclui labels comuns para todos os serviços (app, env, version), que faciliam o controle para diferentes apps e ambientes.

Utilizar secrets como feito nesse exemplo não é recomendado pois pode expor dados sensíveis, como o token de auth para o GHCR. Nesses casos é melhor usar ferramentas como `Sealed Secrets` ou `External Secret`, que são aplicações que rodam no cluster Kubernetes, e são usados para encriptar segredos, tornando mais seguro comitar esses arquivos em repositórios git.
21 changes: 21 additions & 0 deletions kubernetes/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: datapi-deploy
spec:
replicas: 1
selector:
matchLabels:
app: datapi
template:
metadata:
labels:
app: datapi
spec:
containers:
- name: datapi-container
image: ghcr.io/rhrlima/datapi:latest
ports:
- containerPort: 8085
imagePullSecrets:
- name: registry-secret
14 changes: 14 additions & 0 deletions kubernetes/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: datapi

resources:
- namespace.yaml
- deployment.yaml
- secret.yaml

commonLabels:
app: datapi
env: dev
version: v1.0
4 changes: 4 additions & 0 deletions kubernetes/namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: datapi
8 changes: 8 additions & 0 deletions kubernetes/secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: registry-secret
namespace: datapi
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <REDACTED>
1 change: 1 addition & 0 deletions resources/vm.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxIUbdI166bxR4FYr9a3BwBayGQqNl+N7iDbnD0vkG/C4t5OFll2bUysU/7m4durfcDjWUhAENTvJTdNllP88lgGgn9HkzQZq4/X0kjfEcUHj0yyf6B4ffmKgCYteNv3RS1W4nAWvTQOmBmCVLWDuw3ZEBbUnjxqf48KZjVNHq/J+erZ2F0JG4bJLc0qyi34ljbzLe8jqTetxCNqO5oH7264JOZnqJMPSD0q0JpI3GYzlbB5qh+yPwtku7pjbVr9uLTSfryzyNvwqtjbTxXXGquJQLGRtECBUAIkErjprXtKbo6uhyLEGuubF0yqZzvR7rAuz2IlSOlaxJ+E/bqryPuXi7eAgvAqhpAr8LyjqBDHaXDgZLPEWjJhQEfdWkFftGVSUMoom0ZFARbVN7j3jHigShnbOgijL1su/3iieVzq3R/LZXuVjarwES1lB22NMTwnVX/uBYfeNms0LU12n6UrX/vvsvhGHFR1/mlNCs53usodG6VYMLnYT3QeSK/3k= [email protected]
75 changes: 75 additions & 0 deletions terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
resource "azurerm_resource_group" "vm_rg" {
name = "${var.prefix}-${terraform.workspace}-resource-group"
location = var.resource_group_location
}

resource "azurerm_virtual_network" "vm_vnet" {
name = "${var.prefix}-${terraform.workspace}-network"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.vm_rg.location
resource_group_name = azurerm_resource_group.vm_rg.name
}

resource "azurerm_subnet" "vm_subnet" {
name = "${var.prefix}-${terraform.workspace}-subnet"
resource_group_name = azurerm_resource_group.vm_rg.name
virtual_network_name = azurerm_virtual_network.vm_vnet.name
address_prefixes = ["10.0.1.0/24"]
}

resource "azurerm_network_interface" "vm_nic" {
name = "${var.prefix}-${terraform.workspace}-nic"
location = azurerm_resource_group.vm_rg.location
resource_group_name = azurerm_resource_group.vm_rg.name

ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.vm_subnet.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.vm_publicip.id
}
}

resource "azurerm_linux_virtual_machine" "vm" {
name = "${var.prefix}-${terraform.workspace}-machine"
resource_group_name = azurerm_resource_group.vm_rg.name
location = azurerm_resource_group.vm_rg.location
size = "Standard_F2"
admin_username = "adminuser"
network_interface_ids = [
azurerm_network_interface.vm_nic.id,
]

admin_ssh_key {
username = "adminuser"
public_key = file("../resources/vm.pub")
}

os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}

source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts-gen2"
version = "latest"
}
}

resource "random_password" "password" {
length = 20
min_lower = 1
min_upper = 1
min_numeric = 1
min_special = 1
special = true
}

resource "azurerm_public_ip" "vm_publicip" {
name = "${var.prefix}-${terraform.workspace}-publicip"
resource_group_name = azurerm_resource_group.vm_rg.name
location = azurerm_resource_group.vm_rg.location
allocation_method = "Static"
}
12 changes: 12 additions & 0 deletions terraform/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
output "resource_group_name" {
value = azurerm_resource_group.vm_rg.name
}

output "public_ip_address" {
value = azurerm_linux_virtual_machine.vm.public_ip_address
}

output "admin_password" {
sensitive = true
value = azurerm_linux_virtual_machine.vm.admin_password
}
24 changes: 24 additions & 0 deletions terraform/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
terraform {
required_version = ">=1.5"

required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>3.0"
}
random = {
source = "hashicorp/random"
version = "~>3.0"
}
}
backend "azurerm" {
resource_group_name = "tfstate"
storage_account_name = "tfstate24650"
container_name = "tfstate"
key = "datapi/terraform.tfstate"
}
}

provider "azurerm" {
features {}
}
11 changes: 11 additions & 0 deletions terraform/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
variable "resource_group_location" {
type = string
default = "East US"
description = "Location of the resource group."
}

variable "prefix" {
type = string
default = "datapi"
description = "Prefix name for resources."
}