automated terminal push

This commit is contained in:
lenape
2025-07-12 08:51:48 +00:00
parent 751acfe646
commit 0282e18140
10 changed files with 361 additions and 0 deletions

12
Dockerfile Normal file
View File

@@ -0,0 +1,12 @@
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 8080
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "app:app"]

114
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,114 @@
pipeline {
agent any
environment {
GITEA_REPO = 'https://code.jacquesingram.online/lenape/nvhi-atsila-microservice.git'
GITEA_CREDS = '52ee0829-6e65-4951-925b-4186254c3f21'
SONAR_HOST = 'https://sonar.jacquesingram.online'
SONAR_TOKEN = credentials('sonar-token')
ARTIFACTORY_URL = 'https://art.jacquesingram.online/artifactory/nvhi-atsila-docker'
ARTIFACTORY_CREDS = 'artifactory-api'
AWS_CRED_ID = 'aws-ci'
AWS_REGION = 'us-east-2'
TF_BACKEND_BUCKET = 'nvhi-atsila-tf-state'
TF_BACKEND_PREFIX = 'ecs/terraform.tfstate'
TF_DDB_TABLE = 'nvhi-atsila-locks'
SSH_CRED_ID = 'jenkins-ssh'
TF_VAR_cluster_name = 'nvhi-atsila-cluster'
TF_VAR_vpc_cidr = '10.0.0.0/16'
TF_VAR_public_subnets = '10.0.1.0/24,10.0.2.0/24'
TF_VAR_instance_type = 't2.micro'
TF_VAR_key_pair_name = 'nvhi-atsila-deployer'
// Injected from Jenkins Global Env
TF_VAR_jenkins_ip_cidr = env.JENKINS_SSH_CIDR
IMAGE_NAME = 'lenape/nvhi-atsila-microservice'
IMAGE_TAG = "v1.0.${env.BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
git url: env.GITEA_REPO, credentialsId: env.GITEA_CREDS
}
}
stage('SonarQube Scan') {
steps {
withSonarQubeEnv('SonarQube') {
sh "sonar-scanner -Dsonar.projectKey=nvhi-atsila-microservice -Dsonar.sources=."
}
}
}
stage('Build & Push Docker Image') {
steps {
script {
docker.withRegistry(env.ARTIFACTORY_URL, env.ARTIFACTORY_CREDS) {
def img = docker.build("${env.IMAGE_NAME}:${env.IMAGE_TAG}")
img.push()
}
}
}
}
stage('Terraform Init & Apply') {
steps {
withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', credentialsId: env.AWS_CRED_ID]]) {
dir('terraform') {
sh """
terraform init \
-backend-config="bucket=${TF_BACKEND_BUCKET}" \
-backend-config="key=${TF_BACKEND_PREFIX}" \
-backend-config="region=${AWS_REGION}" \
-backend-config="dynamodb_table=${TF_DDB_TABLE}"
terraform apply -auto-approve \
-var="cluster_name=${TF_VAR_cluster_name}" \
-var="vpc_cidr=${TF_VAR_vpc_cidr}" \
-var="public_subnets=${TF_VAR_public_subnets}" \
-var="instance_type=${TF_VAR_instance_type}" \
-var="key_pair_name=${TF_VAR_key_pair_name}" \
-var="jenkins_ip_cidr=${TF_VAR_jenkins_ip_cidr}"
"""
}
}
}
}
stage('Configure EC2 with Ansible') {
steps {
script {
def ec2_ip = sh(
script: "terraform -chdir=terraform output -raw ecs_instance_public_ip",
returnStdout: true
).trim()
writeFile file: 'ansible/hosts', text: "[inventory_hosts]\n${ec2_ip} ansible_user=ubuntu"
}
ansiblePlaybook(
playbook: 'ansible/configure_ecs.yml',
inventory: 'ansible/hosts',
credentialsId: env.SSH_CRED_ID
)
}
}
stage('Register & Deploy to ECS') {
steps {
withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', credentialsId: env.AWS_CRED_ID]]) {
sh """
aws ecs register-task-definition \
--family ${TF_VAR_cluster_name} \
--network-mode bridge \
--container-definitions '[{
"name":"health-workload",
"image":"${env.ARTIFACTORY_URL}/${env.IMAGE_NAME}:${env.IMAGE_TAG}",
"essential":true,
"portMappings":[{"containerPort":8080,"hostPort":8080}]
}]' \
--region ${AWS_REGION}
aws ecs update-service \
--cluster ${TF_VAR_cluster_name} \
--service ${TF_VAR_cluster_name}-service \
--force-new-deployment \
--region ${AWS_REGION}
"""
}
}
}
}
}

39
ansible/configure_ecs.yml Normal file
View File

@@ -0,0 +1,39 @@
---
- name: Configure EC2 for ECS Cluster
hosts: inventory_hosts
become: yes
vars:
ecs_cluster_name: "nvhi-atsila-cluster"
tasks:
- name: Install Docker
apt:
name: docker.io
state: present
update_cache: yes
- name: Start and enable Docker
service:
name: docker
state: started
enabled: true
- name: Write ECS config file
copy:
dest: /etc/ecs/ecs.config
content: |
ECS_CLUSTER={{ ecs_cluster_name }}
- name: Run ECS agent container
docker_container:
name: ecs-agent
image: amazon/amazon-ecs-agent:latest
state: started
restart_policy: always
env_file: /etc/ecs/ecs.config
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/log/ecs:/log
- /var/lib/ecs/data:/data
network_mode: host

2
ansible/hosts Normal file
View File

@@ -0,0 +1,2 @@
[inventory_hosts]
# overwritten dynamically by Jenkins with the EC2 public IP

10
app.py Normal file
View File

@@ -0,0 +1,10 @@
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/health')
def health():
return jsonify(status='OK')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)

1
lenape_key.pub Normal file
View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDFBAOogBj/GHKXQs6FLROGQfXkZe2uKbRron0We7ZOLgt6e1bI7U8IMe+DIH250CHSi4R5DBYFQF5Bk1TkS5cgMtPIAb87vRUGI3sLs29DQA/kllYiZlQi9ejxcEz2+TRWn10Q/Kltlb6ESNLnnnTsIUUxKUeY3MKFFd+V13FleSVLGYondwPWYwD/XJ6a3VwSTJ1wFKO+lpKknSjDl2ZOgYpWFALPH+EwMlRGVMrUXAB604zqR1XOzYXAAWnhmmC9IGgCzU/5JnEgFyhfZbR3kpEH8SmSXahvdFZERp+3j9d3ROjchqnf0Z0zZ7vzX+G+jvzT/jGOkzH9tx0/OqIO9f47OFF8iUfZgUtJU1QGbepdsmQqognhxfJQfMZbVtKUw7zt+mzJz3A0XcRp7IwVHaqJ2QW2dpXi4UbWtejtZqROg6byWq2FpvFGNIT3eiKTf+EpCoOec6YGSrRQlj73Ob0+FhmsyQ6e8KKncaRYx38PqtnWsI3UnLtdKmEJmDBPI0ipxJzmKJKtb0vtJPVYvFEpgiXSwnDX883rAUQrXR/EhOMmbMwk7JSes6/GXH9rWN10JHh1/i1LLpl+rg6VyktFgVBHzVw++y29QSfFixeTvFkkTS5kl//CpKd1GDQb9ZBH6SPgkgOjmASPUo+p5e/NiN/SIBSpYpMjOKs7Q== jacques@Xochiquetzal

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
Flask==2.0.3
gunicorn==20.1.0

8
terraform/backend.tf Normal file
View File

@@ -0,0 +1,8 @@
terraform {
backend "s3" {
bucket = "nvhi-atsila-tf-state"
key = "ecs/terraform.tfstate"
region = "us-east-2"
dynamodb_table = "nvhi-atsila-locks"
}
}

139
terraform/main.tf Normal file
View File

@@ -0,0 +1,139 @@
provider "aws" {
region = var.aws_region
}
data "aws_availability_zones" "azs" {}
# Hardened remote-state S3 bucket
resource "aws_s3_bucket" "tfstate" {
bucket = "nvhi-atsila-tf-state"
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
versioning {
enabled = true
}
tags = {
Name = "nvhi-atsila-tf-state"
Environment = "Production"
}
}
resource "aws_s3_bucket_public_access_block" "tfstate_block" {
bucket = aws_s3_bucket.tfstate.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_dynamodb_table" "locks" {
name = "nvhi-atsila-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
point_in_time_recovery {
enabled = true
}
tags = {
Name = "nvhi-atsila-locks"
Environment = "Production"
}
}
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "${var.cluster_name}-vpc"
}
}
resource "aws_subnet" "public" {
count = length(split(",", var.public_subnets))
vpc_id = aws_vpc.main.id
cidr_block = element(split(",", var.public_subnets), count.index)
availability_zone = data.aws_availability_zones.azs.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.cluster_name}-public-${count.index}"
}
}
resource "aws_security_group" "ecs_sg" {
name = "${var.cluster_name}-sg"
description = "Allow SSH & HTTP to ECS"
vpc_id = aws_vpc.main.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.jenkins_ip_cidr]
}
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.cluster_name}-sg"
}
}
resource "aws_key_pair" "deployer" {
key_name = var.key_pair_name
public_key = file("${path.module}/../lenape_key.pub")
}
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
}
resource "aws_ecs_cluster" "main" {
name = var.cluster_name
}
resource "aws_instance" "ecs_instance" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
subnet_id = aws_subnet.public[0].id
vpc_security_group_ids = [aws_security_group.ecs_sg.id]
key_name = aws_key_pair.deployer.key_name
tags = {
Name = "${var.cluster_name}-instance"
}
}
output "ecs_instance_public_ip" {
value = aws_instance.ecs_instance.public_ip
}

34
terraform/variables.tf Normal file
View File

@@ -0,0 +1,34 @@
variable "aws_region" {
type = string
default = "us-east-2"
}
variable "jenkins_ip_cidr" {
description = "CIDR block for SSH access from Jenkins (injected by pipeline)"
type = string
}
variable "cluster_name" {
description = "Name of the ECS cluster"
type = string
}
variable "vpc_cidr" {
description = "VPC CIDR block"
type = string
}
variable "public_subnets" {
description = "Comma-separated public subnet CIDRs"
type = string
}
variable "instance_type" {
description = "EC2 instance type"
type = string
}
variable "key_pair_name" {
description = "EC2 Key Pair name"
type = string
}