Witaj, w tej już szóstej części omówimy sobie jak za pomocą terraforma wdrożyć naszą aplikację i ją uruchomić na zasobach w digitalocean wykorzystując jedynie terraform do tego. Zapraszam zatem i życzę miłej lektury lub oglądania - materiała jak zawsze dostepny do poczytania i obejrzenia.
Poprzednie części odnajdziesz tutaj:
Materiał na wstęp i część pierwsza a tu wideo.
Część druga a tu wideo
Część trzecia a tu wideo
Część czwarta a tu wideo
Część piąta a tu wideo
Cześć Szósta a tu wideo
By zaoszczędzić - terraform destroy
W komentarzach pod moimi publikacjami czytam pytania o tym dlaczego nie używam przykładowo AWS gdzie jego zasoby są trochę tańsze i jest on bardziej popularny. Szybko odpowiem na to pytania. Tak wiem że AWS jest popularny i w przypadku digitalocean za te same zasoby jest tańszy. Lubie jednak poznawać nowe obszary, a z AWS pracuję zawodowo w pracy po 8h, więc to traktuję jako swoisty reset od zawodowych problemów ;). Plus lubie DigitalOcean, ah ten błękit :).
Dobra wracamy do tematu odcinka / wpisu. Na początek zanim zaczniemy deploy naszej aplikacji muszę zaznaczyć że, na tym etapie, nasze konfiguracje w digitalocean będą obarczone kosztami. Dlatego musimy nauczyć się wydawać polecenie terraform destroy. Tak by niepotrzebne elementy układanki wyłączyć by nie generowały zbędnych kosztów. Tu posłuży nam komenda terraform destroy.
Terrafrom destroy - z parametrami
Polecenie terraform destroy usunie nam wszystko co stworzyliśmy z terraform i znajduje się w naszych plikach konfiguracyjnych oraz w stanie, trzymanym w naszym backend. W tym przypadku jednak nie zależy nam na usunięciu naszego OVH a jedynie zasobów z digitalocean. Zatem wydanie samego terraform destroy byłoby błędem i musimy doprecyzować nasz target.
terraform destroy -target module.digitalocean-youtube
Poprzez tak wprowadzona komendę ukierunkowujemy nasze polecenie terraform destroy na usunięcie zasobów stworzonych przez moduł digitalocean-youtube zdefiniowany w naszym pliku main.tf
module "myovh" {
source = "./modules/myovh"
}
module "digitalocean-youtube" {
source = "./modules/digitalocean/youtube-do"
providers = {
digitalocean = digitalocean.youtube-do
}
digitalocean_project = {
name = "YoutubeProject"
description = "Project for YouTube Channel"
purpose = "Learning"
environment = "development"
}
secound_octet_number = "2"
name_env = "youtube"
app_env = "dev"
}
Zatem nasza konfiguracja ovh będzie bezpieczna. Polecenie przedstawi plan usunięcia naszych zasobów z digitalocean. Liczba 17 będzie tą, która będzie poprawna liczbą związaną z usunięciem moich zasobów z digitalocean. Oczywiście mamy wyżej też podsumowanie w naszej konsoli i możemy je prześledzić i zobaczymy co dokładnie będzie usuwane.
Otrzymujemy też komunikat że -target tak naprawdę nie usunie całego naszego stanu i jest sytuacja wyjatkową oraz zamierzona i oczekujemy konkretnego rezultatu. W moim przypadku będzie to usunięcie 17 zasobów z digitalocean.
UWAGA!!! - Tu musimy uważać, ponieważ jeżeli nasze sieci utworzone przez terraform w naszym koncie były jedynymi sieciami to z automatu stają się one domyślnymi. Sieci domyślnych w DigitalOcean nie można usunąć. Nasz terraform bedzie miał z nimi problem by je usunąć. Rozwiązanie to utworzyć nowe sieci w każdym regionie i ustawić je jako domyślne.
Tu pokazuje nam się siła jaka tkwi w terraform - jest to uniwersalne narzędzie, ale obnażyła się również słabość - jest tak skuteczne jak dobrze jest znana nasza "chmura" na której, uruchamiamy nasz terraform.
Zawsze możemy sprawdzić jakie nasze sieci cloud operator uznał za domyślne (Default).
Ważne też, że jeżeli w digitalocean pracujesz jako zespół w kilka osób to ważne też by dane VPC które, chcemy usunąć nie zawierało żadnego zasobu - tu uwaga na operacje ręczne. Przykładowo można podzielić nasza konfiguracje inaczej rozdzielając VPC na osobny moduł tak by przy naszym terraform destroy nie były usuwane sieci. Jednak to inny nie nasz scenariusz :).
Okej, skoro mamy to wyjaśnione teraz trzeba przed konfiguracja naszych kropelek (droplets) i firewalls postawić z powrotem naszą infrastrukturę. Wydajmy polecenie terraform apply tak by dodać to co skasował nam terraform destroy.
Nim płynnie przyjedziemy do naszej aplikacji i jej uruchomienia po debatujmy jeszcze na temat bugów terraforma w naszej konfiguracji. Brak informacji w data naszego źródła danych spowodowane jest tym że, jeszcze nie mamy informacji o naszym projekcie oraz dlatego też jego pobranie musimy uzależnić od jego powstania - depond_on.
Idziemy do naszego pliku domain.tf i musimy ta informacje uzupełnić.
data "digitalocean_project" "domain" {
depends_on = [digitalocean_project.youtube-project]
name = var.digitalocean_project.name
}
resource "digitalocean_project_resources" "domain" {
project = data.digitalocean_project.domain.id
resources = [
digitalocean_domain.dev-technicznie-nietechnicznie-cloud.urn
]
}
resource "digitalocean_domain" "dev-technicznie-nietechnicznie-cloud" {
name = "${var.app_env}.technicznie-nietechnicznie.cloud"
}
Uzupełniamy wartoscia depends_on = [digitalocean_project.youtube-project], teraz nasz terraform nie powinien zgłaszać błędu. Uruchamiamy terraform plan i nasze poprzednio 17 uśnięte zasoby teraz chcą się dodać na nowo.
Droplet i firewall
Przystąpmy do konfiguracji naszej kropelki (Droplet) tak nazywają się maszyny wirtualne w DigitalOcean. Tworzymy plik np o nazwie droplet.tf
resource "digitalocean_droplet" "web" {
image = "ubuntu-20-04-x64"
name = "web-1"
region = "fra1"
size = "s-1vcpu-1gb"
tags = [data.digitalocean_tag.web_tag.name,data.digitalocean_tag.environment_tag.name]
vpc_uuid = data.digitalocean_vpc.fra1.id
ssh_keys = ["33258272"]
user_data = file("./files/user_data/web_server.yml")
}
data "digitalocean_project" "droplet" {
name = var.digitalocean_project.name
depends_on = [digitalocean_project.youtube-project]
}
resource "digitalocean_project_resources" "droplet" {
project = data.digitalocean_project.droplet.id
depends_on = [digitalocean_project.youtube-project]
resources = [
digitalocean_droplet.web.urn
]
}
resource "digitalocean_record" "web_1" {
domain = digitalocean_domain.dev-technicznie-nietechnicznie-cloud.id
type = "A"
name = "webs-1"
value = digitalocean_droplet.web.ipv4_address
ttl = 60
}
Omówmy zawartość naszego pliku konfiguracyjnego dla zasobu resource "digitalocean_droplet" "web"
image - tu umieszczamy informacje o naszym obrazie jakiego wykorzystamy do uruchomienia naszej maszyny wirtualnej. Listę dostępnych obrazów możemy pobrać poprzez polecenie docli (prywatne i publiczne)
doctl compute image list
zwraca listę naszych prywatnych obrazów
doctl compute image list --public --format ID,NameSlug | less
zwraca listę publicznych obrazów dostarczanych przez digitalocean
name - nazwa dla naszej maszyny wirtualnej
region - region w jakim zostanie uruchomiona nasza maszyna wirtualna
size - jest to rozmiar naszej maszyny wirtualnej określający rodzaj architektury, typ (intel, amd) wielkość dysku czy wielkość ram. Listę tez
doctl compute size list
tags - lista tagów przypiętych do tej maszyny wirtualnej
vpc_uuid - uuid vpc do którego przypniemy nasza maszynę wirtualną, gdy nie podamy zostanie wybrana domyślna dla naszego regionu, vpc powinien się zgadzać z naszym regionem
ssh_keys - lista kluczy publicznych naszych ssh
user_data - dodatkowy plik konfiguracyjny, który wykona się tylko raz na naszym hoscie.
user_data.yml ma bardzo prostą konstrukcję i nie trzeba go tłumaczyć.
#cloud-config
apt_update: true
packages:
- apache2
Kolejne data "digitalocean_project" "droplet" i resource "digitalocean_project_resources" "droplet" to data i resources znane nam z domain.tf.
Nowością jest resource "digitalocean_record" "web_1" - który po prostu dodaj nam nowy rekord do naszego domain. Tym rekordem jest rekord A z adresem IP naszej maszyny wirtualnej (kropelki - droplet).
Konfiguracja naszego firewall będzie znajdować się w pliku firewall.tf
resource "digitalocean_firewall" "managment_firewall" {
name = "tf-${var.digitalocean_project.environment}-${var.name_env}-managment-firewall"
tags = [data.digitalocean_tag.environment_tag.name]
inbound_rule {
protocol = "tcp"
port_range = "22"
source_addresses = var.managment_allows_ips
}
outbound_rule {
protocol = "tcp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "udp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "icmp"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
}
resource "digitalocean_firewall" "web_firewall" {
name = "terraform-${var.digitalocean_project.environment}-${var.name_env}-web-firewall"
tags = [data.digitalocean_tag.web_tag.name]
inbound_rule {
protocol = "tcp"
port_range = "80"
source_addresses = var.managment_allows_ips
}
inbound_rule {
protocol = "tcp"
port_range = "443"
source_addresses = var.managment_allows_ips
}
}
Odpowiednio resource "digitalocean_firewall" "managment_firewall" i resource "digitalocean_firewall" "web_firewall" pola w tym zasobie odpowiadają za:
name - nazwa dla naszego firewall, musi być unikatowa.
tags - tagi maszyn wirtualnych dla których będzie działał ten firewall
inbound_rule - obiekt określający reguły przychodzące
protocol - typ tcp, udp, icmp
port_range - port komunikacyjny
source_addresses lista adresów dla których będzie ta reguła zezwalać na dostęp
outbound_rule obiekt określający reguły wychodzące
Jak możemy zobaczyć nie ma reguł blokujących a zatem tego czego nie dodamy w naszym firewall jest automatycznie blokowane blokowane. Oczywiście jak nie podepniemy firewall do naszej maszyny wirtualnej to komunikacja będzie dozwolona na wszystkich portach i protokołach.
Pozostaje nam tylko uzupełnić nasz plik z tagami które będą generowane na podstawie naszych zmiennych.
data "digitalocean_tag" "environment_tag" {
depends_on = [digitalocean_tag.environment_tag]
name = "web-${var.digitalocean_project.environment}"
}
data "digitalocean_tag" "web_tag" {
depends_on = [digitalocean_tag.web_tag]
name = "web-${var.app_name}"
}
resource "digitalocean_tag" "environment_tag" {
name = "web-${var.digitalocean_project.environment}"
}
resource "digitalocean_tag" "web_tag" {
name = "web-${var.app_name}"
}
Mozemy przystapić do terraform plan i terraform apply
Masza konfiguracja zaczyna się tworzyć i zaraz bedzie dostepna nasza maszyna wirtualna.
Możemy przetestować naszą konfiguracje przechodząc na adres FQDN zdefiniowany w naszym domin. I jak widać wszystko działa:
Uruchamiamy naszą aplikację.
Zróbmy drobna rekonfigurację naszej maszyny wirtualnej by uruchomić teraz naszą aplikację:
resource "digitalocean_droplet" "web" {
image = "docker-20-04"
name = "web-1"
region = "fra1"
size = "s-1vcpu-1gb"
tags = [data.digitalocean_tag.web_tag.name,data.digitalocean_tag.environment_tag.name]
vpc_uuid = data.digitalocean_vpc.fra1.id
ssh_keys = ["33258272"]
#user_data = file("./files/user_data/web_server.yml")
connection {
type = "ssh"
user = "root"
private_key = file("/home/windows/.ssh/id_ed25519_DM")
host = self.ipv4_address
}
provisioner "remote-exec" {
inline = [
"docker run -itd --name web-1 -p 80:80 piotrskoska/web-example"
]
}
}
Dokładnie dodamy dwa nowe obiekty w naszym resources - będą to connection i provisioner które, pomogą nam odpowiednio podłączyć się do maszyny wirtualnej i wykonać na niej zdalna komendę.
docker run -itd --name web-1 -p 80:80 piotrskoska/web-example
czyli uruchomi nam to naszą aplikację w kontenerze z nasłuchującym portem otwrtym na porcie 80.
Teraffrom plan i potem terraform apply i po przekonfigurowaniu naszego środwiska zobaczymy naszą aplikację:
Comments