From aba3a6f47548004234bf54470b16e453080dda2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20J=C4=99dyk?= Date: Wed, 5 Jan 2022 00:38:14 +0100 Subject: [PATCH] 4.3 Hetzner solution --- .gitignore | 142 +------------------------------------------ README.md | 18 +++++- cloud-init | 6 ++ constants.py | 21 +++++++ deploy.py | 155 +++++++++++++++++++++++++++++++++++++++++++++++ remove.py | 27 +++++++++ requirements.txt | 1 + 7 files changed, 229 insertions(+), 141 deletions(-) create mode 100644 cloud-init create mode 100644 constants.py create mode 100644 deploy.py create mode 100644 remove.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index f8b73e7..4164e0d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,140 +1,2 @@ -# ---> Python -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - +__pycache__ +ssh_key.txt \ No newline at end of file diff --git a/README.md b/README.md index c833b4e..e4056e9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,18 @@ -# 434708-PZC-4.3 +## Wymagania +1. Python3 +2. pip3 +## Instrukcja krok po kroku +1. Instalacja wymaganych pakietów. +``` +pip3 install -r requirements.txt +``` +2. Utworzyć plik 'ssh_key.txt' i umieścić w nim klucz, który zostanie użyty do dostępu do maszyny. +3. Uruchomienie skryptu tworzącego maszyny oraz load balancer, jako argument podać klucz API Hetzner. +``` +python3 deploy.py +``` +4. Usunięcie utworzonej zasobów (opcjonalne). +``` +python3 remove.py +``` diff --git a/cloud-init b/cloud-init new file mode 100644 index 0000000..fb495a3 --- /dev/null +++ b/cloud-init @@ -0,0 +1,6 @@ +#cloud-config + +runcmd: + - wget https://git.wmi.amu.edu.pl/s470611/DPZC-aws/raw/branch/master/webservice + - chmod +x webservice + - ./webservice \ No newline at end of file diff --git a/constants.py b/constants.py new file mode 100644 index 0000000..6f23924 --- /dev/null +++ b/constants.py @@ -0,0 +1,21 @@ +SSH_KEY_FNAME = "ssh_key.txt" +CLOUD_INIT_FNAME = "cloud-init" + +SSH_KEY_NAME = "434708-4.3" + +IP_RANGE = "10.10.10.0/24" +NETWORK_ZONE = "eu-central" +NETWORK_TYPE = "cloud" +VNET_NAME = "434708-4.3-vnet" + +SERVER_NAME_1 = "434708-4.3-1" +SERVER_NAME_2 = "434708-4.3-2" + +MACHINE_TYPE = "cpx11" +MACHINE_OS = "ubuntu-20.04" + +LOAD_BALANCER_NAME = "434708-4.3-lb" +LOAD_BALANCER_ALG = "least_connections" +LB_TYPE = "lb11" + +LOCATION = "hel1" \ No newline at end of file diff --git a/deploy.py b/deploy.py new file mode 100644 index 0000000..e82a293 --- /dev/null +++ b/deploy.py @@ -0,0 +1,155 @@ +import sys +from constants import * +from hcloud import Client +from hcloud.locations.domain import Location +from hcloud.networks.domain import NetworkSubnet +from hcloud.images.domain import Image +from hcloud.server_types.domain import ServerType +from hcloud.load_balancer_types.domain import LoadBalancerType +from hcloud.load_balancers.domain import LoadBalancerAlgorithm +from hcloud.load_balancers.domain import LoadBalancerService +from hcloud.load_balancers.domain import LoadBalancerServiceHttp +from hcloud.load_balancers.domain import LoadBalancerHealthCheck +from hcloud.load_balancers.domain import LoadBalancerHealtCheckHttp +from hcloud.load_balancers.domain import LoadBalancerTarget + +with open(SSH_KEY_FNAME, "r") as file: + ssh_key_text = file.read() + +with open(CLOUD_INIT_FNAME, "r") as file: + cloud_init = file.read() + +if len(sys.argv) < 2: + raise ValueError("API key must be provided as an argument!") + +client = Client( + token=str(sys.argv[1]) +) + +# ssh key +if client.ssh_keys.get_by_name(SSH_KEY_NAME): + ssh_key = client.ssh_keys.get_by_name(SSH_KEY_NAME) +else: + ssh_key = client.ssh_keys.create(name=SSH_KEY_NAME, public_key=ssh_key_text) + +print(f"Klucz {ssh_key.data_model.name} został utworzony.") + +# network +if client.networks.get_by_name(VNET_NAME): + vnet = client.networks.get_by_name(VNET_NAME) +else: + vnet = client.networks.create( + name=VNET_NAME, + ip_range=IP_RANGE, + subnets=[ + NetworkSubnet(ip_range=IP_RANGE, network_zone=NETWORK_ZONE, type=NETWORK_TYPE) + ] + ) + +print(f"Utworzono sieć wirtualną: {vnet.data_model.name} ({vnet.data_model.ip_range})") + +# webservice_1 +if(client.servers.get_by_name(SERVER_NAME_1)): + webservice_1 = client.servers.get_by_name(SERVER_NAME_1) +else: + webservice_1_response = client.servers.create( + name=SERVER_NAME_1, + server_type=ServerType(MACHINE_TYPE), + image=Image(name=MACHINE_OS), + ssh_keys=[ssh_key], + networks=[vnet], + location=Location(LOCATION), + user_data=cloud_init + ) + webservice_1_response.action.wait_until_finished() + webservice_1 = webservice_1_response.server + +print(f"Utworzono webservice 1: {webservice_1.data_model.name} ({webservice_1.data_model.public_net.ipv4.ip})") + +# webservice_2 +if(client.servers.get_by_name(SERVER_NAME_2)): + webservice_2 = client.servers.get_by_name(SERVER_NAME_2) +else: + webservice_2_response = client.servers.create( + name=SERVER_NAME_2, + server_type=ServerType(MACHINE_TYPE), + image=Image(name=MACHINE_OS), + ssh_keys=[ssh_key], + networks=[vnet], + location=Location(LOCATION), + user_data=cloud_init + ) + webservice_2_response.action.wait_until_finished() + webservice_2 = webservice_2_response.server + +print(f"Utworzono webservice 2: {webservice_2.data_model.name} ({webservice_2.data_model.public_net.ipv4.ip})") + +# load balance service health check http +lb_health_check_http = LoadBalancerHealtCheckHttp( + path="/factors/10", + status_codes=["2??", "3??"], + tls=False, +) + +# load balance service health check +lb_health_check = LoadBalancerHealthCheck( + protocol="http", + port="8080", + interval=10, + timeout=6, + retries=3, + http=lb_health_check_http +) + +# load balance http +lb_service_http = LoadBalancerServiceHttp( + cookie_name="HCLBSTICKY", + cookie_lifetime="300", + certificates=[], + redirect_http=False, + sticky_sessions=False, +) + +# load balancer service +lb_service = LoadBalancerService( + protocol="http", + listen_port="8080", + destination_port="8080", + proxyprotocol=False, + health_check=lb_health_check, + http=lb_service_http +) + +# webservice_1 target +webservice_1_target = LoadBalancerTarget( + type="server", + server=webservice_1, + use_private_ip=True +) + +# webservice_2 target +webservice_2_target = LoadBalancerTarget( + type="server", + server=webservice_2, + use_private_ip=True +) + +# load balancer +if(client.load_balancers.get_by_name(LOAD_BALANCER_NAME)): + load_balancer = client.load_balancers.get_by_name(LOAD_BALANCER_NAME) +else: + load_balancer_response = client.load_balancers.create( + name=LOAD_BALANCER_NAME, + load_balancer_type=LoadBalancerType(name=LB_TYPE), + location=Location(LOCATION), + algorithm=LoadBalancerAlgorithm(LOAD_BALANCER_ALG), + services=[lb_service], + targets=[webservice_1_target, webservice_2_target], + public_interface=True, + network=vnet + ) + load_balancer_response.action.wait_until_finished() + load_balancer = client.load_balancers.get_by_name(LOAD_BALANCER_NAME) + +print(f"Utworzono load balancer: {load_balancer.data_model.name} ({load_balancer.data_model.public_net.ipv4.ip}:8080)") + diff --git a/remove.py b/remove.py new file mode 100644 index 0000000..7429a5e --- /dev/null +++ b/remove.py @@ -0,0 +1,27 @@ +import sys +from constants import * +from hcloud import Client + +if len(sys.argv) < 2: + raise ValueError("API key must be provided as an argument!") + +client = Client( + token=str(sys.argv[1]) +) + +ssh_key = client.ssh_keys.get_by_name(SSH_KEY_NAME) +vnet = client.networks.get_by_name(VNET_NAME) +webservice_1 = client.servers.get_by_name(SERVER_NAME_1) +webservice_2 = client.servers.get_by_name(SERVER_NAME_2) +load_balancer = client.load_balancers.get_by_name(LOAD_BALANCER_NAME) + +if load_balancer: + load_balancer.delete() +if webservice_1: + webservice_1.delete() +if webservice_2: + webservice_2.delete() +if vnet: + vnet.delete() +if ssh_key: + ssh_key.delete() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c1eafeb --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +hcloud==1.16.0 \ No newline at end of file