automated terminal push
This commit is contained in:
12
Dockerfile
Normal file
12
Dockerfile
Normal 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
114
Jenkinsfile
vendored
Normal 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
39
ansible/configure_ecs.yml
Normal 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
2
ansible/hosts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[inventory_hosts]
|
||||||
|
# overwritten dynamically by Jenkins with the EC2 public IP
|
10
app.py
Normal file
10
app.py
Normal 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
1
lenape_key.pub
Normal 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
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Flask==2.0.3
|
||||||
|
gunicorn==20.1.0
|
8
terraform/backend.tf
Normal file
8
terraform/backend.tf
Normal 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
139
terraform/main.tf
Normal 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
34
terraform/variables.tf
Normal 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
|
||||||
|
}
|
Reference in New Issue
Block a user