Initial commit
This commit is contained in:
commit
e1cd70aab3
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
gitea-key.pem
|
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Gitea uruchamiana automatycznie na środowisku AWS
|
||||||
|
|
||||||
|
Zadania numer `3.1` i `3.2` z przedmiotu *Chmury obliczeniowe* zrealizowane na środowisku AWS.
|
||||||
|
|
||||||
|
## Wymagania wstępne
|
||||||
|
- Konto na platformie AWS
|
||||||
|
- skonfigurowany plik `.aws/credentials` z danymi dostępowymi do AWS
|
||||||
|
|
||||||
|
## Uruchomienie
|
||||||
|
1. Sklonuj repozytorium
|
||||||
|
2. Uruchom skrypt `deploy.sh`:
|
||||||
|
```bash
|
||||||
|
./deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Opcjonalnie można uruchomić skrypt `deploy.py`:
|
||||||
|
```bash
|
||||||
|
python3 deploy.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Zasada działania
|
||||||
|
Skrypt `deploy.py` tworzy sieć VPC, subnety, routing table, security group, 2 instancje EC2 i dodatkowy dysk twardy. Następnie instaluje na jednej z instancji serwer Gitea, a na drugiej bazę danych PostgreSQL (na obu instancjach aplikację działają na dockerze i komunikują się między sobą).
|
||||||
|
|
||||||
|
Skrypt `deploy.sh` uruchamia skrypt `deploy.py`.
|
370
deploy.py
Normal file
370
deploy.py
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
import boto3
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
import platform
|
||||||
|
import paramiko
|
||||||
|
import string
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
REGION = 'us-east-1'
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# ----------------- AWS Connection -----------------
|
||||||
|
# --------------------------------------------------
|
||||||
|
|
||||||
|
def check_aws_connection():
|
||||||
|
try:
|
||||||
|
s3 = boto3.client('s3')
|
||||||
|
s3.list_buckets()
|
||||||
|
print('AWS connection success.')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f'Error: {e}')
|
||||||
|
print('AWS connection failed.')
|
||||||
|
|
||||||
|
check_aws_connection()
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# --------------- Utility functions ----------------
|
||||||
|
# --------------------------------------------------
|
||||||
|
|
||||||
|
def get_public_dns_name(inst):
|
||||||
|
client = boto3.client('ec2', region_name=REGION)
|
||||||
|
response = client.describe_instances(InstanceIds = [inst.id])
|
||||||
|
return response['Reservations'][0]['Instances'][0]['NetworkInterfaces'][0]['Association']['PublicDnsName']
|
||||||
|
|
||||||
|
def get_private_dns_name(inst):
|
||||||
|
client = boto3.client('ec2', region_name=REGION)
|
||||||
|
response = client.describe_instances(InstanceIds = [inst.id])
|
||||||
|
return response['Reservations'][0]['Instances'][0]['NetworkInterfaces'][0]['PrivateDnsName']
|
||||||
|
|
||||||
|
def random_string(length):
|
||||||
|
letters = string.ascii_lowercase
|
||||||
|
return ''.join(random.choice(letters) for i in range(length))
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# ----------------- Create key pair ----------------
|
||||||
|
# --------------------------------------------------
|
||||||
|
|
||||||
|
def create_key_pair(key_name):
|
||||||
|
ec2 = boto3.client('ec2', region_name=REGION)
|
||||||
|
key_pair = ec2.create_key_pair(KeyName=key_name)
|
||||||
|
|
||||||
|
with open(f'{key_name}.pem', 'w') as file:
|
||||||
|
file.write(key_pair['KeyMaterial'])
|
||||||
|
|
||||||
|
return key_pair
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# ------------------ Networking --------------------
|
||||||
|
# --------------------------------------------------
|
||||||
|
|
||||||
|
def get_default_vpc():
|
||||||
|
ec2 = boto3.client('ec2', region_name=REGION)
|
||||||
|
response = ec2.describe_vpcs()
|
||||||
|
vpc_id = response['Vpcs'][0]['VpcId']
|
||||||
|
return vpc_id
|
||||||
|
|
||||||
|
def create_vpc():
|
||||||
|
ec2 = boto3.resource('ec2', region_name=REGION)
|
||||||
|
vpc = ec2.create_vpc(CidrBlock='172.31.0.0/16')
|
||||||
|
|
||||||
|
vpc.modify_attribute(EnableDnsSupport={'Value': True})
|
||||||
|
vpc.modify_attribute(EnableDnsHostnames={'Value': True})
|
||||||
|
|
||||||
|
igw = ec2.create_internet_gateway()
|
||||||
|
vpc.attach_internet_gateway(InternetGatewayId=igw.id)
|
||||||
|
|
||||||
|
return vpc, igw
|
||||||
|
|
||||||
|
def create_subnet(vpc, cidr_block, availability_zone):
|
||||||
|
subnet = vpc.create_subnet(
|
||||||
|
CidrBlock=cidr_block,
|
||||||
|
AvailabilityZone=availability_zone
|
||||||
|
)
|
||||||
|
|
||||||
|
route_table = vpc.create_route_table()
|
||||||
|
route_table.associate_with_subnet(SubnetId=subnet.id)
|
||||||
|
|
||||||
|
route_table.create_route(
|
||||||
|
DestinationCidrBlock='0.0.0.0/0',
|
||||||
|
GatewayId=igw.id
|
||||||
|
)
|
||||||
|
|
||||||
|
return subnet, route_table
|
||||||
|
|
||||||
|
def create_security_group(vpc, group_name, description):
|
||||||
|
ec2 = boto3.resource('ec2', region_name=REGION)
|
||||||
|
security_group = ec2.create_security_group(
|
||||||
|
GroupName=group_name,
|
||||||
|
Description=description,
|
||||||
|
VpcId=vpc.id
|
||||||
|
)
|
||||||
|
|
||||||
|
security_group.authorize_ingress(
|
||||||
|
CidrIp='0.0.0.0/0',
|
||||||
|
IpProtocol='tcp',
|
||||||
|
FromPort=22,
|
||||||
|
ToPort=22
|
||||||
|
)
|
||||||
|
|
||||||
|
security_group.authorize_ingress(
|
||||||
|
CidrIp='0.0.0.0/0',
|
||||||
|
IpProtocol='tcp',
|
||||||
|
FromPort=80,
|
||||||
|
ToPort=80
|
||||||
|
)
|
||||||
|
|
||||||
|
security_group.authorize_ingress(
|
||||||
|
CidrIp='0.0.0.0/0',
|
||||||
|
IpProtocol='tcp',
|
||||||
|
FromPort=443,
|
||||||
|
ToPort=443
|
||||||
|
)
|
||||||
|
|
||||||
|
security_group.authorize_ingress(
|
||||||
|
CidrIp='172.31.0.0/16',
|
||||||
|
IpProtocol='tcp',
|
||||||
|
FromPort=5432,
|
||||||
|
ToPort=5432
|
||||||
|
)
|
||||||
|
|
||||||
|
return security_group
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# ------------ AWS Machine reservation -------------
|
||||||
|
# --------------------------------------------------
|
||||||
|
|
||||||
|
def create_ec2_instance(ami_id, instance_name, security_group_id, subnet_id, key_pair, with_volume=False):
|
||||||
|
ec2 = boto3.resource('ec2', region_name=REGION)
|
||||||
|
|
||||||
|
instance = ec2.create_instances(
|
||||||
|
ImageId=ami_id,
|
||||||
|
InstanceType='t2.micro',
|
||||||
|
MinCount=1,
|
||||||
|
MaxCount=1,
|
||||||
|
NetworkInterfaces=[{
|
||||||
|
'SubnetId': subnet_id,
|
||||||
|
'DeviceIndex': 0,
|
||||||
|
'AssociatePublicIpAddress': True,
|
||||||
|
'DeleteOnTermination': True,
|
||||||
|
'Groups': [security_group_id]
|
||||||
|
}],
|
||||||
|
TagSpecifications=[{'ResourceType': 'instance', 'Tags': [{'Key': 'Name', 'Value': instance_name}]}],
|
||||||
|
KeyName=key_pair['KeyName']
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
instance.wait_until_running()
|
||||||
|
|
||||||
|
if with_volume:
|
||||||
|
volume = ec2.create_volume(
|
||||||
|
AvailabilityZone=instance.placement['AvailabilityZone'],
|
||||||
|
Size=8,
|
||||||
|
VolumeType='gp2',
|
||||||
|
TagSpecifications=[{'ResourceType': 'volume', 'Tags': [{'Key': 'Name', 'Value': f'{instance_name}-volume'}]}]
|
||||||
|
)
|
||||||
|
|
||||||
|
counter = 10
|
||||||
|
while counter > 0:
|
||||||
|
time.sleep(5)
|
||||||
|
try:
|
||||||
|
instance.attach_volume(
|
||||||
|
Device='/dev/sdf',
|
||||||
|
VolumeId=volume.id
|
||||||
|
)
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
if (counter == 1):
|
||||||
|
print(f"Error: {e}")
|
||||||
|
counter -= 1
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
ami_id = 'ami-0ee3dd41c47751fe6'
|
||||||
|
|
||||||
|
# Create a VPC
|
||||||
|
print("[Stage 1/8] Creating VPC...")
|
||||||
|
vpc, igw = create_vpc()
|
||||||
|
|
||||||
|
# Create security group for the instances
|
||||||
|
print("[Stage 2/8] Creating security group...")
|
||||||
|
security_group = create_security_group(vpc, 'GiteaSecurityGroup', 'Gitea Security Group')
|
||||||
|
|
||||||
|
# Create subnets within the VPC for each instance
|
||||||
|
print("[Stage 3/8] Creating subnets...")
|
||||||
|
subnet, route_table = create_subnet(vpc, '172.31.2.0/16', REGION + 'a')
|
||||||
|
|
||||||
|
# Create a key pair for SSH access
|
||||||
|
print("[Stage 4/8] Creating key pair...")
|
||||||
|
key_pair = create_key_pair('gitea-key')
|
||||||
|
|
||||||
|
# Create two EC2 instances within their respective subnets
|
||||||
|
print("[Stage 5/8] Creating EC2 instances (POSTGRES)...")
|
||||||
|
postgres_instance = create_ec2_instance(ami_id, 'PostgresInstance', security_group.group_id, subnet.id, key_pair)
|
||||||
|
print("[Stage 6/8] Creating EC2 instances (GITEA)...")
|
||||||
|
gitea_instance = create_ec2_instance(ami_id, 'GiteaInstance', security_group.group_id, subnet.id, key_pair, True)
|
||||||
|
|
||||||
|
# Get public DNS of the instances
|
||||||
|
gitea_public_dns = get_public_dns_name(gitea_instance)
|
||||||
|
postgres_public_dns = get_public_dns_name(postgres_instance)
|
||||||
|
postgres_private_dns = postgres_instance.private_dns_name
|
||||||
|
|
||||||
|
# Print public DNS of the instances
|
||||||
|
print(f"Postgres instance public DNS: {postgres_public_dns}")
|
||||||
|
print(f"Gitea instance public DNS: {gitea_public_dns}")
|
||||||
|
|
||||||
|
db_password = random_string(16)
|
||||||
|
|
||||||
|
# ssh into the instance and install PostgreSQL
|
||||||
|
print("[Stage 7/8] Configuring Postgres instance...")
|
||||||
|
counter = 5
|
||||||
|
|
||||||
|
while counter > 0:
|
||||||
|
try:
|
||||||
|
print(f"Trying to connect to {postgres_public_dns}...")
|
||||||
|
ssh_client = paramiko.SSHClient()
|
||||||
|
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
ssh_client.connect(postgres_public_dns, username='ec2-user', key_filename=f'{key_pair["KeyName"]}.pem')
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
print("Retrying...")
|
||||||
|
time.sleep(5)
|
||||||
|
counter -= 1
|
||||||
|
|
||||||
|
print("Connected to Postgres instance.")
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo yum update -y')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo yum install -y docker')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo service docker start')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo mkdir postgres-dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo touch postgres-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo chmod 777 postgres-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo echo "FROM postgres:latest" >> postgres-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo echo "ENV POSTGRES_USER gitea" >> postgres-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command(f'sudo echo "ENV POSTGRES_PASSWORD {db_password}" >> postgres-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo echo "ENV POSTGRES_DB gitea" >> postgres-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo docker build -t postgres postgres-dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command(f'sudo docker run -d -p 5432:5432 postgres')
|
||||||
|
|
||||||
|
|
||||||
|
# Configure gitea instance
|
||||||
|
print("[Stage 8/8] Configuring Gitea instance...")
|
||||||
|
counter = 5
|
||||||
|
|
||||||
|
while counter > 0:
|
||||||
|
try:
|
||||||
|
print(f"Trying to connect to {gitea_public_dns}...")
|
||||||
|
ssh_client = paramiko.SSHClient()
|
||||||
|
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
ssh_client.connect(gitea_public_dns, username='ec2-user', key_filename=f'{key_pair["KeyName"]}.pem')
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
print("Retrying...")
|
||||||
|
time.sleep(5)
|
||||||
|
counter -= 1
|
||||||
|
|
||||||
|
print("Connected to Gitea instance.")
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo yum update -y')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo mkfs.ext4 /dev/xvdf')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo mkdir /data')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo mount /dev/xvdf /data')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo mkdir /data/gitea')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo yum install -y docker')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo service docker start')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo mkdir gitea-dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo touch gitea-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo chmod 777 gitea-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo echo "FROM gitea/gitea:latest" >> gitea-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo echo "ENV GITEA__database__DB_TYPE postgres" >> gitea-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command(f'sudo echo "ENV GITEA__database__HOST {postgres_private_dns}:5432" >> gitea-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo echo "ENV GITEA__database__NAME gitea" >> gitea-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo echo "ENV GITEA__database__USER gitea" >> gitea-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command(f'sudo echo "ENV GITEA__database__PASSWD {db_password}" >> gitea-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo echo "ENV GITEA__server__APP_DATA_PATH /data/gitea" >> gitea-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo echo "RUN mkdir /data/gitea" >> gitea-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo echo "EXPOSE 3000" >> gitea-dockerfile/Dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo docker build -t gitea gitea-dockerfile')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
stdin, stdout, stderr = ssh_client.exec_command('sudo docker run -d -p 80:3000 -v /data/gitea:/data/gitea gitea')
|
||||||
|
print(stdout.read().decode())
|
||||||
|
|
||||||
|
print("Gitea and Postgres instances are ready to use.")
|
||||||
|
print(f"Website is available at: http://{gitea_public_dns}")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# Wait for user input to destroy resources
|
||||||
|
input("Press Enter to destroy resources...")
|
||||||
|
|
||||||
|
# Cleanup resources
|
||||||
|
if 'gitea_instance' in locals():
|
||||||
|
print("Terminating Gitea instance...")
|
||||||
|
gitea_instance.terminate()
|
||||||
|
gitea_instance.wait_until_terminated()
|
||||||
|
|
||||||
|
if 'postgres_instance' in locals():
|
||||||
|
print("Terminating Postgres instance...")
|
||||||
|
postgres_instance.terminate()
|
||||||
|
postgres_instance.wait_until_terminated()
|
||||||
|
|
||||||
|
if 'subnet' in locals():
|
||||||
|
print("Deleting subnet...")
|
||||||
|
subnet.delete()
|
||||||
|
|
||||||
|
if 'route_table' in locals():
|
||||||
|
print("Deleting route table...")
|
||||||
|
route_table.delete()
|
||||||
|
|
||||||
|
if 'security_group' in locals():
|
||||||
|
print("Deleting security group...")
|
||||||
|
security_group.delete()
|
||||||
|
|
||||||
|
if 'igw' in locals():
|
||||||
|
print("Detaching and deleting Internet Gateway...")
|
||||||
|
igw.detach_from_vpc(VpcId=vpc.id)
|
||||||
|
igw.delete()
|
||||||
|
|
||||||
|
if 'vpc' in locals():
|
||||||
|
print("Deleting VPC...")
|
||||||
|
vpc.delete()
|
||||||
|
|
||||||
|
if 'key_pair' in locals():
|
||||||
|
print("Deleting key pair...")
|
||||||
|
client = boto3.client('ec2', region_name=REGION)
|
||||||
|
client.delete_key_pair(KeyName=key_pair['KeyName'])
|
||||||
|
|
||||||
|
print("Resources destroyed.")
|
Loading…
Reference in New Issue
Block a user