L02. Pierwsza Virtual Machine i podstawy sieci

Agenda

  1. Stan startowy: środowisko z L01 i kod CloudNotes
  2. Klucz SSH i sprawdzenie dostępności rozmiaru VM
  3. Sieć: Resource Group, VNet, subnet, Network Security Group, publiczny adres IP
  4. Utworzenie maszyny wirtualnej z Linuksem
  5. Połączenie SSH, adres publiczny i prywatny
  6. Uruchomienie CloudNotes na VM
  7. Eksperymenty z regułami NSG
  8. Stop, deallocate i start: obserwacja stanu i kosztów
  9. Koszt maszyny z cennika
  10. Weryfikacja, raport i cleanup

Wprowadzenie

Stan projektu

Po L01 masz skonfigurowany komputer, zalogowane Azure CLI i kod CloudNotes w ~/cloudnotes. W Azure nie ma jeszcze żadnych zasobów kursu.

przed:  Komputer studenta (Azure CLI, ~/cloudnotes, zapisane <SUFFIX> i <REGION>)
        Azure: brak zasobów kursu

w trakcie:
        Internet -> Public IP -> NSG -> VM -> gunicorn :8000 -> CloudNotes

po cleanup:
        Azure: brak zasobów kursu

Na tym laboratorium uruchomisz CloudNotes po raz pierwszy w chmurze, w modelu IaaS. Sam zainstalujesz runtime, uruchomisz serwer aplikacji i otworzysz do niego dostęp z Internetu. Na L03 uruchomisz tę samą aplikację w PaaS i porównasz, ile z tej pracy znika.

Co zrobisz na tym laboratorium

  1. Zbudujesz minimalną sieć: VNet, podsieć i Network Security Group.
  2. Utworzysz maszynę wirtualną z Linuksem i połączysz się z nią przez SSH.
  3. Uruchomisz CloudNotes jako usługę systemową.
  4. Będziesz zmieniać reguły NSG i obserwować skutki.
  5. Porównasz stany stop i deallocate oraz policzysz koszt maszyny.
  6. Usuniesz wszystko jednym poleceniem.

Architektura laboratorium

Internet
   |
   v
pip-lab02 (publiczny IP, statyczny)
   |
   v
+-- rg-lab02, region <REGION> ----------------------------+
|                                                         |
|  vnet-lab02 10.20.0.0/16                                |
|  +-- snet-app 10.20.1.0/24 ---------------------------+ |
|  |  nsg-lab02: allow 22 z <MY_IP>, później allow 8000 | |
|  |                                                    | |
|  |  vm-lab02 (prywatny IP 10.20.1.4)                  | |
|  |    gunicorn :8000 -> CloudNotes                    | |
|  +----------------------------------------------------+ |
+---------------------------------------------------------+

Nazwy zasobów:

ZasóbNazwa
Resource Grouprg-lab02
sieć wirtualnavnet-lab02, przestrzeń adresowa 10.20.0.0/16
podsiećsnet-app, 10.20.1.0/24
Network Security Groupnsg-lab02, przypisana do podsieci
publiczny adres IPpip-lab02, Standard, statyczny
maszyna wirtualnavm-lab02, użytkownik azureuser

Wszystkie zasoby są tymczasowe i znikają na końcu zajęć razem z Resource Group. Kolejne laboratoria nie korzystają z tej maszyny.

Koszt laboratorium

Orientacyjne ceny pay-as-you-go (Sweden Central, październik 2026, w Twoim regionie mogą się nieco różnić):

ZasóbCenaZa 3 godziny
VM Standard_B2ats_v2 z Linuksemokoło 0,0097 USD za godzinęokoło 0,03 USD
publiczny IPv4 Standard, statycznyokoło 0,005 USD za godzinęokoło 0,015 USD
dysk Standard SSD 32 GiBokoło 2,40 USD miesięcznieokoło 0,01 USD
VNet, subnet, NSG, karta sieciowabez opłat0 USD

Razem poniżej 0,10 USD, pod warunkiem że wykonasz cleanup. Ta sama maszyna pozostawiona na miesiąc kosztuje około 13 USD, czyli ponad 10% rocznego kredytu Azure for Students.

Konwencje w poleceniach

  • Polecenia są gotowe do skopiowania do terminala bash. Każde polecenie zawiera pełne nazwy zasobów i wartości. Laboratorium nie używa zmiennych bash, więc możesz w dowolnej chwili otworzyć nowy terminal i kontynuować.
  • Wartości, które musisz wstawić samodzielnie, są zapisane WIELKIMI LITERAMI w nawiasach ostrych. Zastępujesz cały placeholder razem z nawiasami, np. --location <REGION> -> --location swedencentral.
  • Część poleceń wykonujesz na swoim komputerze, a część na maszynie wirtualnej po zalogowaniu przez SSH. Każdy krok mówi wyraźnie, gdzie wykonujesz polecenia. Po zalogowaniu na VM znak zachęty zmienia się na azureuser@vm-lab02.

Placeholdery używane w tym laboratorium:

PlaceholderZnaczenieSkąd wziąć wartość
<REGION>region kursu, np. swedencentralwybrany i zapisany na L01
<MY_IP>publiczny adres IP Twojego komputerapolecenie w kroku 6
<PUBLIC_IP>publiczny adres IP maszyny wirtualnejpolecenie w kroku 7

Nazwa grupy rg-lab02 nie zawiera sufiksu, bo nazwy Resource Groups muszą być unikalne tylko w obrębie Twojej subskrypcji. <SUFFIX> z L01 nie jest na tym laboratorium potrzebny. Zapisz <MY_IP> i <PUBLIC_IP> w notatkach, gdy tylko je odczytasz. Będziesz je wielokrotnie przepisywać do poleceń.


Część A. Przygotowanie

Krok 1. Stan startowy

Cel

Upewnić się, że środowisko z L01 działa, a kod CloudNotes jest aktualny.

Wykonanie

Na swoim komputerze:

az account show --query "{subscription:name, state:state}" --output table

Przygotuj zapisaną na L01 nazwę regionu kursu. W tym laboratorium wstawiasz ją wszędzie tam, gdzie polecenie zawiera <REGION>. Jeśli jej nie pamiętasz, wróć do L01, kroki 14-15, i sprawdź listę dozwolonych regionów.

Zaktualizuj kod projektu:

ls ~/cloudnotes/src

Zarejestruj dostawców zasobów, z których korzysta to laboratorium. Rejestracja jest jednorazowa dla subskrypcji i bezpłatna:

az provider register --namespace Microsoft.Compute --wait
az provider register --namespace Microsoft.Network --wait

Weryfikacja

  • Znasz swój <REGION>.
  • Subskrypcja to Azure for Students w stanie Enabled.
  • Katalog ~/cloudnotes/src zawiera m.in. app.py i requirements.txt.
  • Poniższe polecenie pokazuje stan Registered dla obu dostawców:
az provider list --query "[?namespace=='Microsoft.Compute' || namespace=='Microsoft.Network'].{namespace:namespace, state:registrationState}" --output table

Co właśnie zrobiliśmy?

Sprawdziliśmy kontekst pracy, tak jak na początku każdych zajęć: kim jestem, w której subskrypcji działam i w jakim regionie będę tworzyć zasoby.

Resource provider (dostawca zasobów) to usługa Azure odpowiedzialna za dany rodzaj zasobów, np. Microsoft.Compute za maszyny wirtualne i dyski, a Microsoft.Network za sieci. W nowej subskrypcji część dostawców nie jest zarejestrowana. Dopóki Microsoft.Compute nie jest zarejestrowany, polecenia sprawdzające limity maszyn zwracają puste wyniki, a tworzenie VM może zakończyć się błędem MissingSubscriptionRegistration.

Typowe problemy

ProblemRozwiązanie
Nie wiesz, jaki jest Twój <REGION>.Wróć do L01, kroki 14-15: sprawdź listę dozwolonych regionów i zapisz wybrany region.
Please run 'az login' to setup account.Zaloguj się: az login albo az login --use-device-code (L01, kroki 10-11).
fatal: cannot change to '/home/.../cloudnotes'Brak kodu projektu. Pobierz go: git clone https://github.com/michuhu/cloudnotes.git ~/cloudnotes.
Rejestracja trwa kilka minut.To normalne. Parametr --wait czeka na zakończenie.

Krok 2. Klucz SSH

Cel

Przygotować parę kluczy SSH, którą będziesz logować się do maszyny wirtualnej zamiast hasłem.

Wykonanie

Na swoim komputerze sprawdź, czy masz już klucz Ed25519:

ls -l ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub

Jeśli oba pliki istnieją, przejdź do weryfikacji. Jeśli polecenie zwróciło No such file or directory, wygeneruj klucz:

ssh-keygen -t ed25519 -C "cloudcourse" -f ~/.ssh/id_ed25519

Program zapyta o hasło do klucza (passphrase). Na prywatnym komputerze warto je ustawić. Na komputerze w sali możesz zostawić puste, naciskając dwukrotnie Enter, ale wtedy usuń klucz po zajęciach.

Weryfikacja

ssh-keygen -l -f ~/.ssh/id_ed25519.pub

Wynik zawiera długość klucza, odcisk SHA256:... i typ (ED25519).

Co właśnie zrobiliśmy?

Klucz SSH składa się z dwóch plików:

  • ~/.ssh/id_ed25519 to klucz prywatny. Nigdy go nie kopiujesz ani nie wysyłasz.
  • ~/.ssh/id_ed25519.pub to klucz publiczny. Ten plik przekażemy do Azure, który umieści go na maszynie w ~/.ssh/authorized_keys użytkownika azureuser.

Logowanie kluczem jest bezpieczniejsze niż hasłem: klucza nie da się odgadnąć metodą prób i błędów. Maszyny z publicznym adresem IP i otwartym portem 22 są skanowane przez boty w ciągu minut od utworzenia.

Typowe problemy

ProblemRozwiązanie
ssh-keygen: command not foundZainstaluj klienta SSH: sudo apt-get install -y openssh-client.
Masz tylko klucz RSA ~/.ssh/id_rsa.Możesz wygenerować dodatkowo Ed25519 poleceniem powyżej. Istniejący klucz RSA pozostaje bez zmian.
Overwrite (y/n)?Klucz już istnieje. Odpowiedz n, żeby go nie nadpisać.

Krok 3. Dostępność rozmiaru maszyny w regionie

Cel

Sprawdzić przed utworzeniem zasobów, czy wybrany rozmiar maszyny jest dostępny w Twoim regionie i czy subskrypcja ma na niego limit.

Wykonanie

Na kursie używamy rozmiaru Standard_B2ats_v2: 2 vCPU w modelu burstable, 1 GiB RAM, procesor AMD (x86-64). To najmniejszy i najtańszy rozmiar z aktualnej serii B. Starszy Standard_B1s jest wycofywany i nowe subskrypcje nie mogą go tworzyć (W02).

Do maszyny użyjemy obrazu Canonical:ubuntu-24_04-lts:server:latest, czyli Ubuntu Server 24.04 LTS dla procesorów x86-64.

Sprawdź dostępność rozmiaru. Polecenie może działać do minuty:

az vm list-skus --location <REGION> --resource-type virtualMachines --size Standard_B2ats_v2 --output table

Sprawdź limity vCPU w regionie:

az vm list-usage --location <REGION> --output json |
  jq -r '.[] | select(.name.value | test("^cores|BASv2|BPSv2"; "i")) | "\(.localName): \(.currentValue)/\(.limit)"'

Weryfikacja

Pierwsze polecenie wypisuje wiersz z nazwą Standard_B2ats_v2. Kolumna Restrictions ma wartość None albo ograniczenie typu Zone (np. NotAvailableForSubscription, type: Zone, ... zones: 1,3). Ograniczenie typu Zone oznacza, że rozmiar jest niedostępny tylko w wybranych strefach. Nie wskazujemy strefy, więc to nam nie przeszkadza.

Drugie polecenie pokazuje limity, np.:

Total Regional vCPUs: 0/6
Standard BASv2 Family vCPUs: 0/6

Potrzebujemy 2 wolnych vCPU w obu wierszach.

Wariant awaryjny: ARM64

Jeśli Standard_B2ats_v2 nie pojawia się na liście, ma ograniczenie typu Location albo limit rodziny BASv2 wynosi 0, użyj maszyny z procesorem ARM64:

az vm list-skus --location <REGION> --resource-type virtualMachines --size Standard_B2pts_v2 --output table

Rozmiar Standard_B2pts_v2 ma 2 vCPU i 1 GiB RAM, a obraz Ubuntu musi być w wersji dla ARM64. W wariancie ARM64 zamień w kolejnych poleceniach dwie wartości:

Wartość domyślnaWartość w wariancie ARM64
Standard_B2ats_v2Standard_B2pts_v2
Canonical:ubuntu-24_04-lts:server:latestCanonical:ubuntu-24_04-lts:server-arm64:latest

Dotyczy to polecenia az vm create w kroku 8 i zapytania o cenę maszyny w kroku 20. Pozostałe kroki działają bez zmian. Zapisz w notatkach, którego wariantu używasz. Jeśli i ten rozmiar jest niedostępny, zgłoś to prowadzącemu.

Co właśnie zrobiliśmy?

To jest praktyczna ilustracja dwóch pojęć z W02:

  • capacity: dostawca może nie oferować danego rozmiaru w danym regionie albo strefie,
  • quota: subskrypcja ma limit liczby vCPU w regionie i w każdej rodzinie maszyn.

Sprawdzenie przed utworzeniem zasobu oszczędza czas, bo błąd SkuNotAvailable lub QuotaExceeded pojawiłby się dopiero przy tworzeniu VM.

Typowe problemy

ProblemRozwiązanie
az vm list-usage zwraca pustą listę.Dostawca Microsoft.Compute nie jest zarejestrowany. Wróć do kroku 1.
Lista SKU jest pusta.Rozmiar jest niedostępny dla Twojej subskrypcji w tym regionie. Użyj wariantu ARM64.
Polecenie list-skus trwa długo.To normalne. API zwraca dużo danych. Poczekaj.

Część B. Sieć

Krok 4. Resource Group

Cel

Utworzyć tymczasową Resource Group na wszystkie zasoby laboratorium.

Wykonanie

Na swoim komputerze:

az group create --name rg-lab02 --location <REGION> \
  --tags course=cloud-services lab=l02

Weryfikacja

az group show --name rg-lab02 --query "{name:name, location:location, state:properties.provisioningState, tags:tags}" --output json

Stan Succeeded, dwa tagi: course i lab.

Co właśnie zrobiliśmy?

Wszystko, co utworzymy dalej, trafi do tej grupy. Dzięki temu na końcu usuniemy całe laboratorium jednym poleceniem, tak jak na L01.

Typowe problemy

ProblemRozwiązanie
RequestDisallowedByAzureRegion wstawiony w miejsce <REGION> nie jest dozwolony w Twojej subskrypcji. Sprawdź listę dozwolonych regionów tak jak w L01, krok 14, i wybierz region z tej listy.
-bash: REGION: No such file or directoryNie zastąpiono placeholdera. Bash traktuje < jako przekierowanie wejścia i szuka pliku REGION. Wstaw nazwę regionu w miejsce całego <REGION>, razem z nawiasami.

Krok 5. Sieć wirtualna i podsieć

Cel

Utworzyć prywatną sieć, w której będzie działać maszyna.

Wykonanie

az network vnet create --resource-group rg-lab02 --name vnet-lab02 \
  --location <REGION> --address-prefixes 10.20.0.0/16 \
  --subnet-name snet-app --subnet-prefixes 10.20.1.0/24

Weryfikacja

az network vnet show --resource-group rg-lab02 --name vnet-lab02 \
  --query "{name:name, addressSpace:addressSpace.addressPrefixes}" --output json
az network vnet subnet list --resource-group rg-lab02 --vnet-name vnet-lab02 \
  --query "[].{name:name, prefix:addressPrefix}" --output table

Sieć ma przestrzeń 10.20.0.0/16, a podsieć snet-app zakres 10.20.1.0/24.

Co właśnie zrobiliśmy?

VNet (Virtual Network) to prywatna, odizolowana sieć w chmurze. Zasoby w niej komunikują się prywatnymi adresami IP. Inne sieci, w tym sieci innych klientów, nie widzą jej, dopóki tego jawnie nie skonfigurujemy.

Subnet (podsieć) to fragment przestrzeni adresowej VNet. Podsieci pozwalają oddzielić od siebie grupy zasobów, np. aplikację od bazy danych, i przypisać im różne reguły ruchu.

Zakresy 10.0.0.0/8, 172.16.0.0/12 i 192.168.0.0/16 to prywatne adresy (RFC 1918). Nie są routowane w Internecie. Zakres /16 to 65 536 adresów, /24 to 256 adresów. Azure rezerwuje w każdej podsieci 5 adresów (pierwsze cztery i ostatni), dlatego pierwsza maszyna dostanie adres 10.20.1.4.

Kategoria usługi to Virtual Network. U innych dostawców: AWS VPC, Google Cloud VPC. Szczegóły na W04.

Przy tworzeniu pierwszej sieci w regionie Azure może automatycznie utworzyć grupę NetworkWatcherRG z zasobem Network Watcher. Jest bezpłatny. Nie usuwaj go.

Typowe problemy

ProblemRozwiązanie
MissingSubscriptionRegistration dla Microsoft.Networkaz provider register --namespace Microsoft.Network --wait, potem powtórz polecenie.
ResourceGroupNotFoundGrupa rg-lab02 nie istnieje albo nazwa ma literówkę. Sprawdź az group list --output table i krok 4.

Krok 6. Network Security Group

Cel

Utworzyć zestaw reguł filtrujących ruch, dopuścić SSH wyłącznie z Twojego adresu i przypisać reguły do podsieci.

Wykonanie

Utwórz NSG:

az network nsg create --resource-group rg-lab02 --name nsg-lab02 --location <REGION>

Sprawdź swój publiczny adres IP, czyli adres, pod którym Twój komputer jest widoczny w Internecie:

curl -s https://api.ipify.org; echo

Polecenie wypisuje adres, np. 203.0.113.25. Zapisz go w notatkach. W kolejnych poleceniach jest to <MY_IP>. Zapis <MY_IP>/32 oznacza dokładnie jeden adres, np. 203.0.113.25/32.

Dodaj regułę, która pozwala na SSH (TCP, port 22) tylko z tego adresu:

az network nsg rule create --resource-group rg-lab02 --nsg-name nsg-lab02 \
  --name allow-ssh-my-ip --priority 100 --direction Inbound --access Allow \
  --protocol Tcp --source-address-prefixes <MY_IP>/32 --destination-port-ranges 22

Przypisz NSG do podsieci:

az network vnet subnet update --resource-group rg-lab02 --vnet-name vnet-lab02 \
  --name snet-app --network-security-group nsg-lab02

Weryfikacja

az network nsg rule list --resource-group rg-lab02 --nsg-name nsg-lab02 --include-default \
  --query "[?direction=='Inbound'].{name:name, priority:priority, access:access, source:sourceAddressPrefix, port:destinationPortRange}" \
  --output table

Oczekiwany wynik:

Name                           Priority    Access    Source             Port
-----------------------------  ----------  --------  -----------------  ------
allow-ssh-my-ip                100         Allow     <MY_IP>/32         22
AllowVnetInBound               65000       Allow     VirtualNetwork     *
AllowAzureLoadBalancerInBound  65001       Allow     AzureLoadBalancer  *
DenyAllInBound                 65500       Deny      *                  *
az network vnet subnet show --resource-group rg-lab02 --vnet-name vnet-lab02 \
  --name snet-app --query "networkSecurityGroup.id" --output tsv

Wynik kończy się na /networkSecurityGroups/nsg-lab02.

Co właśnie zrobiliśmy?

Network Security Group to stanowa zapora na poziomie sieci. Zawiera reguły z priorytetem od 100 do 4096 dla reguł użytkownika. Reguły są sprawdzane od najniższego numeru i pierwsza pasująca reguła decyduje. Na końcu zawsze są reguły domyślne, w tym DenyAllInBound: wszystko, czego jawnie nie dopuściliśmy, jest blokowane.

„Stanowa” oznacza, że odpowiedzi na dozwolony ruch przychodzący wracają automatycznie. Nie trzeba dodawać reguły outbound dla odpowiedzi SSH.

Reguła allow-ssh-my-ip realizuje zasadę least privilege na poziomie sieci: port SSH jest otwarty tylko dla jednego adresu, a nie dla całego Internetu.

NSG można przypisać do podsieci albo do karty sieciowej maszyny. Przypisanie do podsieci chroni wszystkie maszyny w tej podsieci jednym zestawem reguł. Odpowiedniki u innych dostawców: AWS Security Groups i Network ACL, Google Cloud VPC firewall rules.

Typowe problemy

ProblemRozwiązanie
curl -s https://api.ipify.org nic nie wypisuje.Brak dostępu do api.ipify.org. Otwórz https://api.ipify.org w przeglądarce i przepisz adres stamtąd.
-bash: MY_IP: No such file or directoryNie zastąpiono placeholdera. Bash traktuje < jako przekierowanie wejścia i szuka pliku MY_IP. Wstaw swój adres w miejsce całego <MY_IP>, np. 203.0.113.25/32.
Reguła ma błędne źródło, np. literówkę w adresie.Usuń regułę: az network nsg rule delete --resource-group rg-lab02 --nsg-name nsg-lab02 --name allow-ssh-my-ip i utwórz ją ponownie z poprawnym adresem.
SecurityRuleConflict lub błąd priorytetuInna reguła w tym samym kierunku ma już ten priorytet. Użyj innego numeru.

Krok 7. Publiczny adres IP

Cel

Utworzyć statyczny publiczny adres IPv4, przez który maszyna będzie dostępna z Internetu.

Wykonanie

az network public-ip create --resource-group rg-lab02 --name pip-lab02 \
  --location <REGION> --sku Standard --allocation-method Static --version IPv4

Wyświetl przydzielony adres:

az network public-ip show --resource-group rg-lab02 --name pip-lab02 --query ipAddress --output tsv

Polecenie wypisuje adres, np. 198.51.100.17. Zapisz go w notatkach. W kolejnych poleceniach jest to <PUBLIC_IP>. Jeśli zgubisz zapisaną wartość, wykonaj to polecenie ponownie, także w nowym terminalu.

Weryfikacja

az network public-ip show --resource-group rg-lab02 --name pip-lab02 \
  --query "{ip:ipAddress, sku:sku.name, allocation:publicIPAllocationMethod}" --output table

Adres ma wartość, SKU Standard, przydział Static.

Co właśnie zrobiliśmy?

Publiczny adres IP to osobny zasób z osobnym rozliczeniem: około 0,005 USD za każdą godzinę istnienia, niezależnie od tego, czy maszyna działa (W02). Adres statyczny nie zmienia się przy zatrzymaniu i ponownym uruchomieniu maszyny. Zobaczysz to w części E.

Utworzenie adresu przed maszyną pozwala znać go zawczasu, np. żeby wpisać go do DNS. W praktyce wiele systemów w ogóle nie nadaje maszynom publicznych adresów. Ruch z Internetu przechodzi przez load balancer lub bramę, a maszyny mają tylko adresy prywatne. Na tym laboratorium upraszczamy architekturę, żeby zobaczyć podstawowe elementy.

Typowe problemy

ProblemRozwiązanie
Polecenie az network public-ip show nic nie wypisuje.Adres jeszcze się tworzy. Powtórz polecenie po kilku sekundach.
PublicIPCountLimitReachedSubskrypcja osiągnęła limit adresów publicznych w regionie. Sprawdź, czy nie zostały adresy z innych ćwiczeń: az network public-ip list --output table.

Część C. Maszyna wirtualna

Krok 8. Utworzenie VM

Cel

Utworzyć maszynę wirtualną z Ubuntu Server 24.04 LTS w przygotowanej podsieci, z przygotowanym publicznym adresem i kluczem SSH.

Wykonanie

Jeśli w kroku 3 przeszedłeś na wariant ARM64, zamień w poleceniu Standard_B2ats_v2 na Standard_B2pts_v2 oraz Canonical:ubuntu-24_04-lts:server:latest na Canonical:ubuntu-24_04-lts:server-arm64:latest.

Utwórz maszynę. Polecenie time zmierzy, ile trwa provisioning:

time az vm create --resource-group rg-lab02 --name vm-lab02 \
  --location <REGION> --image Canonical:ubuntu-24_04-lts:server:latest --size Standard_B2ats_v2 \
  --admin-username azureuser --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name vnet-lab02 --subnet snet-app --nsg "" \
  --public-ip-address pip-lab02 \
  --os-disk-size-gb 30 --storage-sku StandardSSD_LRS

Znaczenie najważniejszych parametrów:

ParametrZnaczenie
--imageobraz systemu w formacie wydawca:oferta:sku:wersja
--sizerozmiar maszyny, czyli liczba vCPU i ilość pamięci
--admin-username, --ssh-key-valuesużytkownik i klucz publiczny. Logowanie hasłem jest wyłączone.
--vnet-name, --subnetistniejąca sieć i podsieć z kroku 5
--nsg ""nie twórz osobnego NSG na karcie sieciowej, bo NSG jest już przypisany do podsieci
--public-ip-addressistniejący adres z kroku 7
--os-disk-size-gb, --storage-skudysk systemowy 30 GiB typu Standard SSD

Weryfikacja

Polecenie zwraca JSON, w którym powerState ma wartość VM running, publicIpAddress jest równy zapisanemu <PUBLIC_IP>, a privateIpAddress to 10.20.1.4. Zanotuj czas real z wyniku time.

az vm show --resource-group rg-lab02 --name vm-lab02 --show-details \
  --query "{size:hardwareProfile.vmSize, power:powerState, publicIp:publicIps, privateIp:privateIps}" \
  --output table

Co właśnie zrobiliśmy?

Maszyna była gotowa po kilku minutach. W modelu z własną serwerownią (W01) ten sam efekt wymagał zamówienia i montażu sprzętu. To jest provisioning w chmurze.

Utworzyliśmy maszynę w modelu IaaS: dostawca dał nam wirtualny sprzęt i obraz systemu. Za wszystko powyżej, czyli aktualizacje systemu, runtime, aplikację i konfigurację, odpowiadamy sami.

Kategoria usługi: Virtual Machine. Implementacje: Azure Virtual Machines, AWS EC2, Google Compute Engine.

Typowe problemy

ProblemRozwiązanie
SkuNotAvailableRozmiar jest niedostępny w regionie. Użyj wariantu ARM64 z kroku 3 i powtórz polecenie.
QuotaExceeded lub OperationNotAllowed z informacją o limicie vCPUBrak limitu dla tej rodziny maszyn. Sprawdź krok 3, użyj wariantu ARM64 albo zgłoś problem prowadzącemu. Nie wnioskuj o zwiększenie limitu przez upgrade subskrypcji.
AllocationFailedChwilowy brak sprzętu w regionie. Usuń częściowo utworzoną maszynę poleceniem az vm delete --resource-group rg-lab02 --name vm-lab02 --yes i spróbuj ponownie za kilka minut albo w wariancie ARM64.
The image architecture does not match the VM sizeWartości --image i --size są z różnych wariantów. Użyj obu wartości z jednego wariantu z kroku 3.
An RSA key file or key value must be supplied lub błąd odczytu kluczaPlik ~/.ssh/id_ed25519.pub nie istnieje. Wróć do kroku 2.

Krok 9. Obserwacja zasobów

Cel

Zobaczyć, z ilu zasobów składa się „jedna maszyna wirtualna” i które z nich są płatne.

Wykonanie

az resource list --resource-group rg-lab02 \
  --query "[].{name:name, type:type}" --output table

Otwórz także w Azure Portal grupę rg-lab02 i kliknij maszynę vm-lab02. Na stronie Overview znajdź rozmiar, adres publiczny, adres prywatny, sieć i podsieć. W menu Networking (lub Network settings) zobacz reguły NSG działające na tę maszynę.

Weryfikacja

Lista zawiera sześć zasobów:

ZasóbTypPłatny?
vm-lab02Microsoft.Compute/virtualMachinestak, za godzinę działania
vm-lab02_OsDisk_1_...Microsoft.Compute/diskstak, za pojemność, dopóki istnieje
vm-lab02VMNicMicrosoft.Network/networkInterfacesnie
pip-lab02Microsoft.Network/publicIPAddressestak, za godzinę istnienia
nsg-lab02Microsoft.Network/networkSecurityGroupsnie
vnet-lab02Microsoft.Network/virtualNetworksnie

Co właśnie zrobiliśmy?

„Maszyna wirtualna” to w rzeczywistości kilka zasobów o osobnym cyklu życia i osobnym rozliczeniu. To bezpośrednio odpowiada rachunkowi z W02: compute, dysk i adres IP to trzy pozycje. Usunięcie samej VM nie musi usuwać dysku, karty sieciowej i adresu IP. Dlatego na kursie usuwamy całą Resource Group.

Typowe problemy

ProblemRozwiązanie
Na liście jest dodatkowy NSG o nazwie vm-lab02NSG.W poleceniu z kroku 8 zabrakło --nsg "". Maszyna ma wtedy dwie warstwy reguł: na podsieci i na karcie sieciowej. Ruch musi przejść przez obie. Dla porządku powtórz laboratorium od kroku 8 po usunięciu VM albo pamiętaj, żeby w eksperymentach dodawać reguły w obu NSG.

Krok 10. Połączenie SSH

Cel

Zalogować się na maszynę.

Wykonanie

Na swoim komputerze:

ssh azureuser@<PUBLIC_IP>

Przy pierwszym połączeniu SSH pokaże odcisk klucza serwera i zapyta:

Are you sure you want to continue connecting (yes/no/[fingerprint])?

Wpisz yes. Jeśli ustawiłeś hasło do klucza, SSH o nie zapyta.

Weryfikacja

Znak zachęty zmienia się na:

azureuser@vm-lab02:~$

Co właśnie zrobiliśmy?

Połączenie przeszło przez Internet do publicznego adresu, przez regułę allow-ssh-my-ip w NSG, do maszyny. Maszyna sprawdziła, czy Twój klucz publiczny jest w ~/.ssh/authorized_keys, a Twój klient SSH zapisał odcisk klucza serwera w ~/.ssh/known_hosts. Przy kolejnych połączeniach klient sprawdzi, czy łączy się z tą samą maszyną.

Typowe problemy

ProblemRozwiązanie
Connection timed outZmienił się Twój publiczny adres (np. inna sieć Wi-Fi) albo sieć blokuje port 22. Sprawdź adres poleceniem curl -s https://api.ipify.org. Jeśli jest inny niż zapisany <MY_IP>, zaktualizuj regułę, wstawiając nowy adres: az network nsg rule update --resource-group rg-lab02 --nsg-name nsg-lab02 --name allow-ssh-my-ip --source-address-prefixes <MY_IP>/32.
Permission denied (publickey)Klient używa innego klucza. Wskaż go jawnie: ssh -i ~/.ssh/id_ed25519 azureuser@<PUBLIC_IP>.
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!Ten sam publiczny adres był wcześniej przypisany innej maszynie. Usuń stary wpis: ssh-keygen -R <PUBLIC_IP> i połącz się ponownie.
Sieć uczelniana blokuje port 22.Użyj Azure Cloud Shell (L01, krok 5). Cloud Shell ma inny publiczny adres, więc dodaj go do reguły tak jak w pierwszym wierszu tabeli i wygeneruj w Cloud Shell osobny klucz. Najprościej poproś prowadzącego o pomoc.

Krok 11. Adres publiczny i prywatny

Cel

Zobaczyć, który adres widzi system operacyjny maszyny, a który świat zewnętrzny.

Wykonanie

Na maszynie wirtualnej:

hostname
grep PRETTY_NAME /etc/os-release
ip -4 address show eth0
curl -s https://api.ipify.org; echo
nproc
free -h
df -h /

Na swoim komputerze w drugim terminalu:

az vm list-ip-addresses --resource-group rg-lab02 --name vm-lab02 --output table

Weryfikacja

  • ip -4 address show eth0 pokazuje adres 10.20.1.4/24. Nie ma tam adresu publicznego.
  • curl -s https://api.ipify.org na VM zwraca adres równy zapisanemu <PUBLIC_IP>.
  • nproc zwraca 2, free -h pokazuje około 1 GiB pamięci, df -h / dysk około 30 GB.

Co właśnie zrobiliśmy?

System operacyjny maszyny zna wyłącznie adres prywatny z podsieci. Adres publiczny istnieje na brzegu sieci Azure, która tłumaczy go na adres prywatny (NAT). Dla ruchu wychodzącego działa to w drugą stronę: serwis api.ipify.org widzi połączenie z adresu publicznego.

Konsekwencje:

  • inne zasoby w tej samej sieci VNet komunikują się z maszyną po adresie prywatnym, bez wychodzenia do Internetu,
  • maszyna bez publicznego adresu jest nieosiągalna z Internetu, nawet gdyby NSG na to pozwalał,
  • konfigurując aplikację na VM, nasłuchujemy na adresie prywatnym albo na 0.0.0.0, a nie na adresie publicznym.

Typowe problemy

ProblemRozwiązanie
Interfejs nazywa się inaczej niż eth0.Wyświetl wszystkie: ip -4 address.
curl na VM nie zwraca adresu.Maszyna nie ma dostępu do Internetu. Sprawdź, czy w kroku 8 przypisano --public-ip-address pip-lab02.

Część D. Aplikacja na VM

Krok 12. Instalacja CloudNotes

Cel

Zainstalować na maszynie wszystko, czego potrzebuje aplikacja.

Wykonanie

Na maszynie wirtualnej:

sudo apt-get update
sudo apt-get install -y python3-venv git
git clone https://github.com/michuhu/cloudnotes.git ~/cloudnotes
cd ~/cloudnotes/src
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

Instalacja zależności trwa zwykle od jednej do dwóch minut.

Weryfikacja

.venv/bin/python --version
.venv/bin/gunicorn --version

Python w wersji 3.12, Gunicorn w wersji 23 lub nowszej.

Co właśnie zrobiliśmy?

To jest praca, którą w modelu IaaS wykonuje klient: aktualizacja indeksu pakietów, instalacja runtime, pobranie kodu, instalacja zależności. Ten sam zestaw czynności trzeba powtarzać na każdej nowej maszynie, a w przyszłości regularnie instalować aktualizacje bezpieczeństwa systemu. Na L03 App Service zrobi to za nas.

Typowe problemy

ProblemRozwiązanie
Could not get lock /var/lib/dpkg/lock-frontendTuż po starcie maszyny system instaluje aktualizacje automatycznie. Poczekaj chwilę i powtórz polecenie.
The virtual environment was not created successfully because ensurepip is not availablePakiet python3-venv nie został zainstalowany. Powtórz sudo apt-get install -y python3-venv.
Sesja SSH zerwała się podczas pip install.Połącz się ponownie i powtórz cd ~/cloudnotes/src oraz .venv/bin/pip install -r requirements.txt.
fatal: destination path '/home/azureuser/cloudnotes' already existsKod jest już pobrany. Przejdź dalej.

Krok 13. CloudNotes jako usługa systemowa

Cel

Uruchomić aplikację tak, żeby działała w tle, po wylogowaniu i po restarcie maszyny.

Wykonanie

Na maszynie wirtualnej utwórz definicję usługi systemd:

sudo tee /etc/systemd/system/cloudnotes.service > /dev/null <<'EOF'
[Unit]
Description=CloudNotes
After=network-online.target
Wants=network-online.target

[Service]
User=azureuser
WorkingDirectory=/home/azureuser/cloudnotes/src
Environment=APP_ENVIRONMENT=vm
ExecStart=/home/azureuser/cloudnotes/src/.venv/bin/gunicorn --bind 0.0.0.0:8000 app:app
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

Włącz i uruchom usługę:

sudo systemctl daemon-reload
sudo systemctl enable --now cloudnotes

Weryfikacja

systemctl status cloudnotes --no-pager
curl -s http://localhost:8000/health | python3 -m json.tool

Status usługi to active (running). Odpowiedź /health wygląda podobnie do:

{
    "backends": {
        "database": "sqlite",
        "files": "local",
        "reports": "sync",
        "secrets": "environment",
        "telemetry": "off"
    },
    "checks": {
        "database": "ok",
        "files": "ok",
        "queue": "ok",
        "reports": "ok"
    },
    "environment": "vm",
    "instance": "vm-lab02",
    "requests_handled": 1,
    "started_at": "2026-10-13 10:15:42 UTC",
    "status": "healthy"
}

Dodaj notatkę przez API i sprawdź listę:

curl -s -o /dev/null -w "%{http_code}\n" -X POST -F title="Pierwsza notatka" -F content="Notatka zapisana na VM" http://localhost:8000/notes
curl -s http://localhost:8000/api/notes

Pierwsze polecenie zwraca 302 (przekierowanie po zapisaniu), drugie listę z jedną notatką.

Logi aplikacji:

journalctl -u cloudnotes -n 20 --no-pager

Co właśnie zrobiliśmy?

  • Gunicorn to produkcyjny serwer aplikacji Python. Nasłuchuje na 0.0.0.0:8000, czyli na wszystkich interfejsach maszyny, również na adresie prywatnym 10.20.1.4.
  • systemd uruchamia aplikację przy starcie systemu (enable) i restartuje ją po awarii (Restart=on-failure).
  • Konfiguracja jest poza kodem: linia Environment= w definicji usługi ustawia zmienną środowiskową procesu aplikacji. APP_ENVIRONMENT=vm to nazwa środowiska, którą aplikacja pokazuje w interfejsie i w /health. Adres i port nasłuchu (0.0.0.0:8000) podajemy Gunicornowi w --bind. Nic z tego nie jest zapisane w kodzie aplikacji. Ten sam kod uruchomimy na L03 w App Service z inną konfiguracją, ustawioną przez App Settings.
  • backends pokazuje, że aplikacja działa w trybie lokalnym: notatki w SQLite, pliki na dysku. Dane leżą w katalogu tymczasowym /tmp/cloudnotes na dysku maszyny. Zapamiętaj to, wrócimy do tego w kroku 19.

Na tym laboratorium aplikacja działa po zwykłym HTTP, bez TLS i bez reverse proxy (np. nginx). Na produkcyjnej maszynie trzeba by skonfigurować oba elementy oraz certyfikat. W PaaS (L03) HTTPS dostaniemy od platformy.

Typowe problemy

ProblemRozwiązanie
Status failed, w logach No such file or directoryŚcieżka w pliku usługi jest błędna, najczęściej brak .venv. Sprawdź ls ~/cloudnotes/src/.venv/bin/gunicorn, popraw plik i wykonaj sudo systemctl daemon-reload oraz sudo systemctl restart cloudnotes.
W logach ModuleNotFoundErrorZależności nie zostały zainstalowane. Powtórz krok 12.
curl: (7) Failed to connect to localhost port 8000Usługa nie działa albo jeszcze startuje. Sprawdź journalctl -u cloudnotes -n 50 --no-pager.

Krok 14. Test z Internetu

Cel

Sprawdzić, czy aplikacja, która działa na maszynie, jest dostępna z Twojego komputera.

Wykonanie

Na swoim komputerze (w drugim terminalu albo po wpisaniu exit w sesji SSH):

curl -sS --max-time 5 http://<PUBLIC_IP>:8000/health

Otwórz także adres http://<PUBLIC_IP>:8000 w przeglądarce.

Weryfikacja

curl kończy się po 5 sekundach komunikatem podobnym do:

curl: (28) Connection timed out after 5002 milliseconds

Przeglądarka długo ładuje stronę i kończy błędem przekroczenia czasu.

Co właśnie zrobiliśmy?

Aplikacja działa (krok 13 pokazał odpowiedź z localhost), ale nie jest dostępna z Internetu. Pakiety do portu 8000 zostały odrzucone przez regułę DenyAllInBound w NSG, zanim dotarły do maszyny. NSG po cichu odrzuca pakiety, więc klient nie dostaje żadnej odpowiedzi i czeka do przekroczenia czasu.

To ważna lekcja diagnostyczna: „aplikacja działa” i „aplikacja jest dostępna dla użytkowników” to dwa różne stwierdzenia. Między użytkownikiem a aplikacją jest sieć z własnymi regułami.

Typowe problemy

ProblemRozwiązanie
curl od razu zwraca stronę.Na liście reguł jest reguła dopuszczająca port 8000 albo NSG nie jest przypisany do podsieci. Sprawdź krok 6 i krok 9.
-bash: PUBLIC_IP: No such file or directoryNie zastąpiono placeholdera. Wstaw adres maszyny w miejsce całego <PUBLIC_IP>. Adres wyświetlisz poleceniem z kroku 7.

Część E. Eksperymenty z regułami NSG

Krok 15. Otwarcie portu aplikacji

Cel

Dopuścić ruch z Internetu do portu 8000 i zobaczyć działającą aplikację w przeglądarce.

Wykonanie

Na swoim komputerze:

az network nsg rule create --resource-group rg-lab02 --nsg-name nsg-lab02 \
  --name allow-http-8000 --priority 110 --direction Inbound --access Allow \
  --protocol Tcp --source-address-prefixes Internet --destination-port-ranges 8000

Po kilku sekundach:

curl -sS --max-time 5 http://<PUBLIC_IP>:8000/health

Weryfikacja

  • curl zwraca JSON z "status":"healthy" i "environment":"vm".
  • W przeglądarce pod adresem http://<PUBLIC_IP>:8000 widać CloudNotes z notatką z kroku 13.
  • Dodaj w przeglądarce drugą notatkę, np. „Notatka przed restartem”. Stopka strony pokazuje nazwę instancji vm-lab02 i czas jej uruchomienia.

Co właśnie zrobiliśmy?

Reguła z priorytetem 110 pasuje do ruchu TCP na port 8000 z dowolnego adresu w Internecie (Internet to service tag, czyli nazwana grupa adresów zarządzana przez Azure). Jest sprawdzana przed DenyAllInBound (65500), więc ruch przechodzi.

Zwróć uwagę na asymetrię reguł: SSH jest otwarte tylko dla Twojego adresu, a aplikacja dla wszystkich. Tak powinno być. Port administracyjny ma być maksymalnie ograniczony, a port usługi publicznej musi być dostępny dla użytkowników.

Typowe problemy

ProblemRozwiązanie
Nadal timeout.Odczekaj kilkanaście sekund, reguły propagują się z opóźnieniem. Sprawdź, czy reguła ma port 8000 i kierunek Inbound. Sieć uczelniana może blokować ruch wychodzący na nietypowe porty: sprawdź z telefonu przez sieć komórkową albo z Cloud Shell.
curl: (7) ... Connection refusedRuch dociera do maszyny, ale aplikacja nie nasłuchuje. Sprawdź usługę na VM: systemctl status cloudnotes --no-pager.

Krok 16. Priorytety reguł

Cel

Zobaczyć, że o wyniku decyduje priorytet, a nie kolejność dodania reguł.

Wykonanie

Dodaj regułę blokującą port 8000 z niższym numerem, czyli wyższym priorytetem niż allow-http-8000:

az network nsg rule create --resource-group rg-lab02 --nsg-name nsg-lab02 \
  --name deny-http-8000 --priority 105 --direction Inbound --access Deny \
  --protocol Tcp --source-address-prefixes Internet --destination-port-ranges 8000

Po kilku sekundach przetestuj:

curl -sS --max-time 5 http://<PUBLIC_IP>:8000/health

Zmień priorytet reguły blokującej na 120, czyli niższy niż allow-http-8000:

az network nsg rule update --resource-group rg-lab02 --nsg-name nsg-lab02 \
  --name deny-http-8000 --priority 120

Przetestuj ponownie:

curl -sS --max-time 5 http://<PUBLIC_IP>:8000/health

Usuń regułę blokującą:

az network nsg rule delete --resource-group rg-lab02 --nsg-name nsg-lab02 --name deny-http-8000

Weryfikacja

Stan regułWynik curl
deny-http-8000 105, allow-http-8000 110Connection timed out
allow-http-8000 110, deny-http-8000 120JSON z /health
tylko allow-http-8000JSON z /health

Na koniec lista reguł użytkownika:

az network nsg rule list --resource-group rg-lab02 --nsg-name nsg-lab02 \
  --query "[].{name:name, priority:priority, access:access, source:sourceAddressPrefix, port:destinationPortRange}" \
  --output table

zawiera allow-ssh-my-ip i allow-http-8000.

Co właśnie zrobiliśmy?

NSG ocenia reguły od najniższego numeru i zatrzymuje się na pierwszej pasującej. Reguła deny-http-8000 z priorytetem 105 blokowała ruch, mimo że reguła dopuszczająca istniała. Po zmianie priorytetu na 120 reguła blokująca nadal istnieje, ale nigdy nie jest sprawdzana dla tego ruchu, bo wcześniej pasuje allow-http-8000.

W praktyce reguły blokujące z wysokim priorytetem stosuje się np. do szybkiego odcięcia adresu, z którego przychodzi atak.

Typowe problemy

ProblemRozwiązanie
Wynik nie zmienia się od razu.Zmiany w NSG propagują się kilka do kilkunastu sekund. Powtórz curl po chwili.
SecurityRuleConflict przy priorytecie 105 lub 120Inna reguła ma już ten priorytet. Wybierz inny, zachowując relację względem 110.

Krok 17. Timeout a connection refused

Cel

Nauczyć się odróżniać blokadę sieciową od niedziałającej aplikacji.

Wykonanie

Na swoim komputerze zatrzymaj aplikację na maszynie przez SSH:

ssh azureuser@<PUBLIC_IP> sudo systemctl stop cloudnotes
curl -sS --max-time 5 http://<PUBLIC_IP>:8000/health

Uruchom aplikację ponownie:

ssh azureuser@<PUBLIC_IP> sudo systemctl start cloudnotes
curl -sS --max-time 5 http://<PUBLIC_IP>:8000/health

Weryfikacja

Przy zatrzymanej aplikacji curl od razu zwraca:

curl: (7) Failed to connect to <PUBLIC_IP> port 8000 after 30 ms: Couldn't connect to server

albo komunikat z Connection refused. Po uruchomieniu aplikacji wraca odpowiedź JSON.

Co właśnie zrobiliśmy?

ObjawCo się dziejeGdzie szukać przyczyny
Connection timed outpakiety giną po drodze, nikt nie odpowiadaNSG, firewall, routing, zatrzymana maszyna, brak publicznego IP
Connection refusedpakiet dotarł do maszyny, ale na porcie nic nie nasłuchujeaplikacja nie działa, zły port, aplikacja nasłuchuje tylko na 127.0.0.1
odpowiedź HTTP z błędem, np. 500aplikacja działa i odpowiada, ale ma problemlogi aplikacji

Ta tabela przyda się na wszystkich kolejnych laboratoriach, a szczególnie na L07 i L09.

Typowe problemy

ProblemRozwiązanie
ssh pyta o hasło sudo.Na maszynach Azure użytkownik administracyjny ma sudo bez hasła. Upewnij się, że logujesz się jako azureuser.
Po start nadal refused.Aplikacja startuje kilka sekund. Powtórz curl po chwili.

Część F. Stop, deallocate i koszty

Krok 18. Stop

Cel

Zatrzymać maszynę poleceniem stop i sprawdzić, co to oznacza dla kosztów.

Wykonanie

Upewnij się, że w CloudNotes są co najmniej dwie notatki (krok 13 i krok 15). Następnie na swoim komputerze:

az vm stop --resource-group rg-lab02 --name vm-lab02

Sprawdź stan:

az vm show --resource-group rg-lab02 --name vm-lab02 --show-details \
  --query "{power:powerState, publicIp:publicIps, privateIp:privateIps}" --output table
curl -sS --max-time 5 http://<PUBLIC_IP>:8000/health

Weryfikacja

  • Azure CLI wyświetla ostrzeżenie, że maszyna nadal będzie rozliczana, i sugeruje az vm deallocate.
  • powerState ma wartość VM stopped.
  • curl kończy się przekroczeniem czasu.

Co właśnie zrobiliśmy?

az vm stop wyłącza system operacyjny, ale Azure nadal rezerwuje dla maszyny sprzęt w data center. Dlatego compute jest naliczany tak samo jak przy działającej maszynie. Ten sam stan powstaje, gdy wyłączysz system z wnętrza maszyny, np. poleceniem sudo shutdown now.

Typowe problemy

ProblemRozwiązanie
Polecenie trwa długo.Zatrzymanie trwa zwykle do minuty. Poczekaj na zakończenie.

Krok 19. Deallocate i start

Cel

Zwolnić sprzęt maszyny, uruchomić ją ponownie i sprawdzić, co przetrwało.

Wykonanie

az vm deallocate --resource-group rg-lab02 --name vm-lab02
az vm show --resource-group rg-lab02 --name vm-lab02 --show-details --query powerState --output tsv
az network public-ip show --resource-group rg-lab02 --name pip-lab02 --query ipAddress --output tsv
az resource list --resource-group rg-lab02 --query "[].{name:name, type:type}" --output table

Uruchom maszynę:

az vm start --resource-group rg-lab02 --name vm-lab02
az vm show --resource-group rg-lab02 --name vm-lab02 --show-details --query powerState --output tsv

Po około minucie sprawdź aplikację:

curl -sS --max-time 10 http://<PUBLIC_IP>:8000/health
curl -sS --max-time 10 http://<PUBLIC_IP>:8000/api/notes

Otwórz CloudNotes w przeglądarce.

Weryfikacja

ObserwacjaWynik
stan po deallocateVM deallocated
publiczny adres po deallocateten sam co zapisany <PUBLIC_IP>
zasoby w grupie po deallocatenadal sześć, w tym dysk i publiczny adres
stan po startVM running
aplikacja po startdziała, bo usługa systemd jest włączona
started_at w /healthnowy czas
notatkilista jest pusta

Co właśnie zrobiliśmy?

Deallocate zwalnia sprzęt. Od tej chwili nie płacimy za compute, ale dysk i statyczny publiczny adres nadal istnieją i nadal kosztują (W02). Adres IP się nie zmienił, bo jest statyczny. Gdyby był dynamiczny, po deallocate mógłby zostać zwolniony.

Aplikacja wróciła sama, bo skonfigurowaliśmy ją jako usługę systemową. Za to notatki zniknęły. Dlaczego?

  • aplikacja w trybie lokalnym przechowuje dane w katalogu tymczasowym /tmp/cloudnotes,
  • Ubuntu czyści katalog /tmp przy każdym starcie systemu,
  • po starcie aplikacja utworzyła nową, pustą bazę SQLite.

Dysk maszyny przetrwał, ale stan aplikacji nie, bo był przechowywany w miejscu związanym z życiem instancji. Ten problem jest celowy i jest jednym z głównych wątków kursu. Rozwiązanie (dane poza instancją compute: Object Storage i Managed Database) pojawi się na W05, L04 i L05. Na L03 zobaczysz ten sam efekt w App Service.

Porównanie stanów:

StanComputeDyskStatyczny IPDane w /tmp
Runningpłacimypłacimypłacimysą
Stoppedpłacimypłacimypłacimysą do restartu
Deallocatednie płacimypłacimypłacimyznikają przy starcie
usuniętenie płacimynie płacimynie płacimy-

Typowe problemy

ProblemRozwiązanie
curl po start zwraca timeout.System i aplikacja startują około minuty. Spróbuj ponownie.
AllocationFailed przy startPo deallocate Azure musi ponownie znaleźć wolny sprzęt i chwilowo może go nie mieć. Spróbuj za kilka minut. To praktyczny przykład, że deallocate oddaje zasoby dostawcy.
Notatki nie zniknęły.Sprawdź started_at w /health. Jeśli czas jest stary, maszyna nie została zrestartowana, np. pominięto deallocate.

Krok 20. Koszt maszyny z cennika

Cel

Wyliczyć miesięczny koszt swojej maszyny dla swojego regionu na podstawie publicznego cennika.

Wykonanie

Azure udostępnia publiczne API cennika, które nie wymaga logowania. Na swoim komputerze pobierz ceny trzech płatnych zasobów. Każde polecenie wypisuje gotowy wynik, a koszt miesięczny liczy przy założeniu 730 godzin w miesiącu.

Parametr $filter w poleceniach to nazwa parametru zapytania API (składnia OData), a nie zmienna bash. Znak \ przed $ sprawia, że bash przekazuje go do curl bez zmian.

Cena maszyny wirtualnej:

curl -s -G https://prices.azure.com/api/retail/prices \
  --data-urlencode "\$filter=serviceName eq 'Virtual Machines' and armRegionName eq '<REGION>' and armSkuName eq 'Standard_B2ats_v2' and priceType eq 'Consumption'" |
  jq -r '[.Items[] | select(.productName | startswith("Virtual Machines") and endswith("Series")) | select(.skuName | test("Spot|Low Priority") | not)][0] | "VM: \(.retailPrice) USD/h, \(.retailPrice * 730 * 100 | round / 100) USD/miesiąc"'

Jeśli używasz wariantu ARM64, zamień w tym poleceniu Standard_B2ats_v2 na Standard_B2pts_v2. Filtr w jq wybiera cenę maszyny z Linuksem: pomija warianty Windows, Spot i Low Priority.

Cena statycznego publicznego adresu IPv4:

curl -s -G https://prices.azure.com/api/retail/prices \
  --data-urlencode "\$filter=serviceName eq 'Virtual Network' and armRegionName eq '<REGION>' and meterName eq 'Standard IPv4 Static Public IP' and priceType eq 'Consumption'" |
  jq -r '.Items[0] | "IP: \(.retailPrice) USD/h, \(.retailPrice * 730 * 100 | round / 100) USD/miesiąc"'

Cena dysku Standard SSD 32 GiB (poziom E4). Dysk jest rozliczany miesięcznie, więc cena z API jest już ceną za miesiąc:

curl -s -G https://prices.azure.com/api/retail/prices \
  --data-urlencode "\$filter=serviceName eq 'Storage' and armRegionName eq '<REGION>' and meterName eq 'E4 LRS Disk' and priceType eq 'Consumption'" |
  jq -r '.Items[0] | "Dysk: \(.retailPrice) USD/miesiąc"'

Zapisz trzy kwoty miesięczne w notatkach i policz samodzielnie:

  • koszt maszyny w stanie Running lub Stopped = VM + IP + dysk,
  • koszt maszyny w stanie Deallocated = IP + dysk.

Weryfikacja

Wszystkie trzy polecenia wypisują wartości liczbowe. Dla Sweden Central wynik wygląda tak:

VM: 0.00972 USD/h, 7.1 USD/miesiąc
IP: 0.005 USD/h, 3.65 USD/miesiąc
Dysk: 2.4 USD/miesiąc

Sumy dla Sweden Central: Running lub Stopped około 13,15 USD miesięcznie, Deallocated około 6,05 USD miesięcznie. Twoje wartości mogą się różnić zależnie od regionu.

Sprawdź też kredyt w Azure Sponsorships portal albo w Education Hub (L01, krok 3). Koszty dzisiejszego laboratorium pojawią się tam z opóźnieniem, zwykle następnego dnia.

Co właśnie zrobiliśmy?

Policzyliśmy rachunek z W02 dla własnej maszyny. Dysk jest rozliczany według poziomu pojemności: dysk 30 GiB mieści się w poziomie E4 (32 GiB). Ceny są cenami katalogowymi pay-as-you-go w USD i nie uwzględniają darmowych godzin, które mogą przysługiwać w niektórych ofertach.

Najważniejszy wniosek: deallocate zmniejsza koszt, ale go nie zeruje. Zero daje dopiero usunięcie zasobów.

Typowe problemy

ProblemRozwiązanie
Polecenie nic nie wypisuje albo wypisuje null.Zapytanie nie znalazło ceny. Najczęściej region ma literówkę albo nie jest nazwą techniczną (swedencentral, a nie Sweden Central). Sprawdź pisownię regionu i rozmiaru maszyny.
jq: error ... roundBardzo stara wersja jq. Sprawdź jq --version. Wersja 1.6 lub nowsza obsługuje round. Na starszej wersji usuń z polecenia fragment `* 730 * 100
Placeholder <REGION> został w poleceniu.Wewnątrz cudzysłowów bash nie zgłasza błędu, ale API nie znajdzie regionu <REGION> i polecenie wypisze null. Wstaw nazwę regionu.

Weryfikacja końcowa

Wykonaj na swoim komputerze, zanim usuniesz zasoby. Maszyna musi działać (VM running).

Polecenie zawiera dwa rodzaje placeholderów:

  • <PUBLIC_IP>: publiczny adres maszyny z kroku 7,
  • <KOSZT_VM>, <KOSZT_IP>, <KOSZT_DYSK>: miesięczne kwoty w USD z kroku 20, np. 7.1, 3.65, 2.4.

Region i rozmiar maszyny raport odczytuje sam z Azure.

{
  echo "L02 report"
  date -u +%Y-%m-%dT%H:%M:%SZ
  echo "--- region and size"
  az vm show --resource-group rg-lab02 --name vm-lab02 --query "{region:location, size:hardwareProfile.vmSize}" -o table
  echo "--- resources"
  az resource list --resource-group rg-lab02 --query "[].{name:name, type:type}" -o table
  echo "--- ip addresses"
  az vm list-ip-addresses --resource-group rg-lab02 --name vm-lab02 -o table
  echo "--- nsg rules"
  az network nsg rule list --resource-group rg-lab02 --nsg-name nsg-lab02 --query "[].{name:name, priority:priority, access:access, port:destinationPortRange}" -o table
  echo "--- health from laptop"
  curl -sS --max-time 10 http://<PUBLIC_IP>:8000/health
  echo
  echo "--- monthly cost estimate"
  echo "VM=<KOSZT_VM> IP=<KOSZT_IP> DISK=<KOSZT_DYSK> USD"
} > ~/l02-report.txt
cat ~/l02-report.txt

Sprawdź w raporcie:

  • Lista zasobów zawiera VM, dysk, kartę sieciową, publiczny adres, NSG i VNet.
  • Adres prywatny to 10.20.1.4, publiczny jest równy <PUBLIC_IP>.
  • Reguły NSG to allow-ssh-my-ip (100) i allow-http-8000 (110), bez deny-http-8000.
  • Odpowiedź /health ma "status":"healthy" i "environment":"vm".
  • Koszty miesięczne mają wartości liczbowe, a nie placeholdery.

Raport zawiera nazwy zasobów i reguły NSG, ale nie zawiera sekretów ani kluczy. Nie dołączaj do niego zawartości katalogu ~/.ssh.

Co powinieneś rozumieć po laboratorium

  • Że maszyna wirtualna działa wewnątrz sieci wirtualnej i podsieci, a „jedna VM” to kilka osobnych zasobów.
  • Że system operacyjny maszyny zna tylko adres prywatny, a adres publiczny jest tłumaczony na brzegu sieci dostawcy.
  • Że NSG filtruje ruch, zanim dotrze do maszyny, reguły są oceniane według priorytetu, a domyślnie ruch przychodzący z Internetu jest blokowany.
  • Że port administracyjny (SSH) należy ograniczać do znanych adresów.
  • Jak odróżnić timeout (blokada po drodze) od connection refused (brak usługi na porcie).
  • Że w IaaS to klient instaluje runtime, uruchamia aplikację, dba o jej start po restarcie i o aktualizacje systemu.
  • Że stop nie zatrzymuje kosztu compute, deallocate go zatrzymuje, a dysk i statyczny adres IP kosztują, dopóki istnieją.
  • Że dane zapisane lokalnie na instancji compute mogą zniknąć przy restarcie, nawet jeśli dysk przetrwał.

Cleanup

Usuń całą grupę laboratorium. Operacji nie da się cofnąć, więc najpierw sprawdź nazwę:

az resource list --resource-group rg-lab02 --query "[].name" --output tsv
az group delete --name rg-lab02 --yes --no-wait
az group wait --name rg-lab02 --deleted

Sprawdź wynik:

az group exists --name rg-lab02
az group list --tag lab=l02 --query "[].name" --output tsv
az network public-ip list --query "[].{name:name, group:resourceGroup, ip:ipAddress}" --output table
az disk list --query "[].{name:name, group:resourceGroup}" --output table

Oczekiwane wyniki: false, pusta lista grup z tagiem lab=l02, brak publicznych adresów i dysków z tego laboratorium.

Dopisz wynik do raportu:

echo "--- rg-lab02 exists after cleanup" >> ~/l02-report.txt
az group exists --name rg-lab02 >> ~/l02-report.txt
tail -n 2 ~/l02-report.txt

Ostatnia linia raportu to false.

Usuń wpis maszyny z known_hosts. Ten sam adres może w przyszłości dostać inna maszyna:

ssh-keygen -R <PUBLIC_IP>

Grupa NetworkWatcherRG, jeśli powstała automatycznie, zawiera bezpłatny Network Watcher. Nie musisz jej usuwać.

Nie usuwaj klucza ~/.ssh/id_ed25519 ani katalogu ~/cloudnotes. Wyjątek: jeśli pracujesz na współdzielonym komputerze w sali i klucz nie ma hasła, usuń go poleceniem rm ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub i wyloguj się z Azure poleceniem az logout.

Zasoby do utrzymania po laboratorium: brak.

Zadanie do oddania

Oddaj w sposób wskazany przez prowadzącego:

  1. Plik ~/l02-report.txt z sekcji Weryfikacja końcowa, z dopisanym wynikiem cleanup (rg-lab02 exists after cleanup i false).
  2. Krótkie odpowiedzi, po 2-4 zdania, na pytania:
    1. Dlaczego ip address na maszynie pokazuje adres 10.20.1.4, a nie adres publiczny? Jak w takim razie ruch z Internetu trafia do maszyny?
    2. Reguła deny-http-8000 z priorytetem 120 istniała jednocześnie z allow-http-8000 z priorytetem 110. Dlaczego aplikacja była dostępna?
    3. Użytkownik zgłasza, że „strona nie działa”. curl zwraca Connection refused. Gdzie szukasz przyczyny, a gdzie nie musisz?
    4. Które składniki kosztu Twojej maszyny pozostają w stanie Deallocated? Ile kosztowałaby miesięcznie zwolniona maszyna w Twoim regionie?
    5. Dlaczego notatki zniknęły po deallocate i start, skoro dysk maszyny przetrwał? Co trzeba zmienić w architekturze, żeby dane przetrwały?

Źródła i materiały dodatkowe

 

Imię Nazwisko

OPIS