Agenda
- Stan startowy: środowisko z L01 i kod CloudNotes
- Klucz SSH i sprawdzenie dostępności rozmiaru VM
- Sieć: Resource Group, VNet, subnet, Network Security Group, publiczny adres IP
- Utworzenie maszyny wirtualnej z Linuksem
- Połączenie SSH, adres publiczny i prywatny
- Uruchomienie CloudNotes na VM
- Eksperymenty z regułami NSG
- Stop, deallocate i start: obserwacja stanu i kosztów
- Koszt maszyny z cennika
- 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
- Zbudujesz minimalną sieć: VNet, podsieć i Network Security Group.
- Utworzysz maszynę wirtualną z Linuksem i połączysz się z nią przez SSH.
- Uruchomisz CloudNotes jako usługę systemową.
- Będziesz zmieniać reguły NSG i obserwować skutki.
- Porównasz stany
stopideallocateoraz policzysz koszt maszyny. - 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ób | Nazwa |
|---|---|
| Resource Group | rg-lab02 |
| sieć wirtualna | vnet-lab02, przestrzeń adresowa 10.20.0.0/16 |
| podsieć | snet-app, 10.20.1.0/24 |
| Network Security Group | nsg-lab02, przypisana do podsieci |
| publiczny adres IP | pip-lab02, Standard, statyczny |
| maszyna wirtualna | vm-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ób | Cena | Za 3 godziny |
|---|---|---|
VM Standard_B2ats_v2 z Linuksem | około 0,0097 USD za godzinę | około 0,03 USD |
| publiczny IPv4 Standard, statyczny | około 0,005 USD za godzinę | około 0,015 USD |
| dysk Standard SSD 32 GiB | około 2,40 USD miesięcznie | około 0,01 USD |
| VNet, subnet, NSG, karta sieciowa | bez opłat | 0 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:
| Placeholder | Znaczenie | Skąd wziąć wartość |
|---|---|---|
<REGION> | region kursu, np. swedencentral | wybrany i zapisany na L01 |
<MY_IP> | publiczny adres IP Twojego komputera | polecenie w kroku 6 |
<PUBLIC_IP> | publiczny adres IP maszyny wirtualnej | polecenie 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 Studentsw stanieEnabled. - Katalog
~/cloudnotes/srczawiera m.in.app.pyirequirements.txt. - Poniższe polecenie pokazuje stan
Registereddla 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
| Problem | Rozwią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_ed25519to klucz prywatny. Nigdy go nie kopiujesz ani nie wysyłasz.~/.ssh/id_ed25519.pubto klucz publiczny. Ten plik przekażemy do Azure, który umieści go na maszynie w~/.ssh/authorized_keysużytkownikaazureuser.
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
| Problem | Rozwiązanie |
|---|---|
ssh-keygen: command not found | Zainstaluj 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ślna | Wartość w wariancie ARM64 |
|---|---|
Standard_B2ats_v2 | Standard_B2pts_v2 |
Canonical:ubuntu-24_04-lts:server:latest | Canonical: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
| Problem | Rozwią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
| Problem | Rozwiązanie |
|---|---|
RequestDisallowedByAzure | Region 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 directory | Nie 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
| Problem | Rozwiązanie |
|---|---|
MissingSubscriptionRegistration dla Microsoft.Network | az provider register --namespace Microsoft.Network --wait, potem powtórz polecenie. |
ResourceGroupNotFound | Grupa 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
| Problem | Rozwią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 directory | Nie 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 priorytetu | Inna 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
| Problem | Rozwiązanie |
|---|---|
Polecenie az network public-ip show nic nie wypisuje. | Adres jeszcze się tworzy. Powtórz polecenie po kilku sekundach. |
PublicIPCountLimitReached | Subskrypcja 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:
| Parametr | Znaczenie |
|---|---|
--image | obraz systemu w formacie wydawca:oferta:sku:wersja |
--size | rozmiar maszyny, czyli liczba vCPU i ilość pamięci |
--admin-username, --ssh-key-values | użytkownik i klucz publiczny. Logowanie hasłem jest wyłączone. |
--vnet-name, --subnet | istnieją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-address | istniejący adres z kroku 7 |
--os-disk-size-gb, --storage-sku | dysk 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
| Problem | Rozwiązanie |
|---|---|
SkuNotAvailable | Rozmiar jest niedostępny w regionie. Użyj wariantu ARM64 z kroku 3 i powtórz polecenie. |
QuotaExceeded lub OperationNotAllowed z informacją o limicie vCPU | Brak 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. |
AllocationFailed | Chwilowy 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 size | Wartoś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 klucza | Plik ~/.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ób | Typ | Płatny? |
|---|---|---|
vm-lab02 | Microsoft.Compute/virtualMachines | tak, za godzinę działania |
vm-lab02_OsDisk_1_... | Microsoft.Compute/disks | tak, za pojemność, dopóki istnieje |
vm-lab02VMNic | Microsoft.Network/networkInterfaces | nie |
pip-lab02 | Microsoft.Network/publicIPAddresses | tak, za godzinę istnienia |
nsg-lab02 | Microsoft.Network/networkSecurityGroups | nie |
vnet-lab02 | Microsoft.Network/virtualNetworks | nie |
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
| Problem | Rozwią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
| Problem | Rozwiązanie |
|---|---|
Connection timed out | Zmienił 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 eth0pokazuje adres10.20.1.4/24. Nie ma tam adresu publicznego.curl -s https://api.ipify.orgna VM zwraca adres równy zapisanemu<PUBLIC_IP>.nproczwraca2,free -hpokazuje 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
| Problem | Rozwią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
| Problem | Rozwiązanie |
|---|---|
Could not get lock /var/lib/dpkg/lock-frontend | Tuż po starcie maszyny system instaluje aktualizacje automatycznie. Poczekaj chwilę i powtórz polecenie. |
The virtual environment was not created successfully because ensurepip is not available | Pakiet 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 exists | Kod 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 prywatnym10.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=vmto 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. backendspokazuje, że aplikacja działa w trybie lokalnym: notatki w SQLite, pliki na dysku. Dane leżą w katalogu tymczasowym/tmp/cloudnotesna 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
| Problem | Rozwią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 ModuleNotFoundError | Zależności nie zostały zainstalowane. Powtórz krok 12. |
curl: (7) Failed to connect to localhost port 8000 | Usł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
| Problem | Rozwią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 directory | Nie 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
curlzwraca JSON z"status":"healthy"i"environment":"vm".- W przeglądarce pod adresem
http://<PUBLIC_IP>:8000widać CloudNotes z notatką z kroku 13. - Dodaj w przeglądarce drugą notatkę, np. „Notatka przed restartem”. Stopka strony pokazuje nazwę instancji
vm-lab02i 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
| Problem | Rozwią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 refused | Ruch 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 110 | Connection timed out |
allow-http-8000 110, deny-http-8000 120 | JSON z /health |
tylko allow-http-8000 | JSON 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
| Problem | Rozwią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 120 | Inna 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?
| Objaw | Co się dzieje | Gdzie szukać przyczyny |
|---|---|---|
Connection timed out | pakiety giną po drodze, nikt nie odpowiada | NSG, firewall, routing, zatrzymana maszyna, brak publicznego IP |
Connection refused | pakiet dotarł do maszyny, ale na porcie nic nie nasłuchuje | aplikacja nie działa, zły port, aplikacja nasłuchuje tylko na 127.0.0.1 |
odpowiedź HTTP z błędem, np. 500 | aplikacja działa i odpowiada, ale ma problem | logi aplikacji |
Ta tabela przyda się na wszystkich kolejnych laboratoriach, a szczególnie na L07 i L09.
Typowe problemy
| Problem | Rozwią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. powerStatema wartośćVM stopped.curlkoń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
| Problem | Rozwią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
| Obserwacja | Wynik |
|---|---|
stan po deallocate | VM deallocated |
publiczny adres po deallocate | ten sam co zapisany <PUBLIC_IP> |
zasoby w grupie po deallocate | nadal sześć, w tym dysk i publiczny adres |
stan po start | VM running |
aplikacja po start | działa, bo usługa systemd jest włączona |
started_at w /health | nowy czas |
| notatki | lista 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
/tmpprzy 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:
| Stan | Compute | Dysk | Statyczny IP | Dane w /tmp |
|---|---|---|---|---|
| Running | płacimy | płacimy | płacimy | są |
| Stopped | płacimy | płacimy | płacimy | są do restartu |
| Deallocated | nie płacimy | płacimy | płacimy | znikają przy starcie |
| usunięte | nie płacimy | nie płacimy | nie płacimy | - |
Typowe problemy
| Problem | Rozwiązanie |
|---|---|
curl po start zwraca timeout. | System i aplikacja startują około minuty. Spróbuj ponownie. |
AllocationFailed przy start | Po 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
RunninglubStopped= 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
| Problem | Rozwią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 ... round | Bardzo 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) iallow-http-8000(110), bezdeny-http-8000. - Odpowiedź
/healthma"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
stopnie zatrzymuje kosztu compute,deallocatego 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:
- Plik
~/l02-report.txtz sekcji Weryfikacja końcowa, z dopisanym wynikiem cleanup (rg-lab02 exists after cleanupifalse). - Krótkie odpowiedzi, po 2-4 zdania, na pytania:
- Dlaczego
ip addressna maszynie pokazuje adres10.20.1.4, a nie adres publiczny? Jak w takim razie ruch z Internetu trafia do maszyny? - Reguła
deny-http-8000z priorytetem 120 istniała jednocześnie zallow-http-8000z priorytetem 110. Dlaczego aplikacja była dostępna? - Użytkownik zgłasza, że „strona nie działa”.
curlzwracaConnection refused. Gdzie szukasz przyczyny, a gdzie nie musisz? - Które składniki kosztu Twojej maszyny pozostają w stanie
Deallocated? Ile kosztowałaby miesięcznie zwolniona maszyna w Twoim regionie? - Dlaczego notatki zniknęły po
deallocateistart, skoro dysk maszyny przetrwał? Co trzeba zmienić w architekturze, żeby dane przetrwały?
- Dlaczego
Źródła i materiały dodatkowe
- Microsoft Learn, Quickstart: Create a Linux virtual machine with the Azure CLI: https://learn.microsoft.com/azure/virtual-machines/linux/quick-create-cli
- Microsoft Learn, az vm reference: https://learn.microsoft.com/cli/azure/vm
- Microsoft Learn, States and billing status of Azure Virtual Machines: https://learn.microsoft.com/azure/virtual-machines/states-billing
- Microsoft Learn, Basv2 size series: https://learn.microsoft.com/azure/virtual-machines/sizes/general-purpose/basv2-series
- Microsoft Learn, Bpsv2 size series: https://learn.microsoft.com/azure/virtual-machines/sizes/general-purpose/bpsv2-series
- Microsoft Learn, VM size series retirements and capacity growth restrictions: https://learn.microsoft.com/azure/virtual-machines/sizes/lifecycle/retirements-and-capacity-restrictions
- Microsoft Learn, Resolve errors for SKU not available: https://learn.microsoft.com/azure/azure-resource-manager/troubleshooting/error-sku-not-available
- Microsoft Learn, Check vCPU quotas: https://learn.microsoft.com/azure/virtual-machines/quotas
- Microsoft Learn, Azure resource providers and types: https://learn.microsoft.com/azure/azure-resource-manager/management/resource-providers-and-types
- Microsoft Learn, What is Azure Virtual Network?: https://learn.microsoft.com/azure/virtual-network/virtual-networks-overview
- Microsoft Learn, Network security groups: https://learn.microsoft.com/azure/virtual-network/network-security-groups-overview
- Microsoft Learn, Virtual network service tags: https://learn.microsoft.com/azure/virtual-network/service-tags-overview
- Microsoft Learn, Public IP addresses in Azure: https://learn.microsoft.com/azure/virtual-network/ip-services/public-ip-addresses
- Microsoft Learn, Private IP addresses in Azure: https://learn.microsoft.com/azure/virtual-network/ip-services/private-ip-addresses
- Microsoft Learn, Create and manage SSH keys for Linux VMs: https://learn.microsoft.com/azure/virtual-machines/linux/create-ssh-keys-detailed
- Microsoft Learn, Azure Retail Prices REST API: https://learn.microsoft.com/rest/api/cost-management/retail-prices/azure-retail-prices
- Azure, Linux Virtual Machines pricing: https://azure.microsoft.com/pricing/details/virtual-machines/linux/
- Azure, IP addresses pricing: https://azure.microsoft.com/pricing/details/ip-addresses/
- Azure, Managed Disks pricing: https://azure.microsoft.com/pricing/details/managed-disks/
- systemd.service manual: https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html