Strona główna / Artykuły / Lokalna rozwój funkcji Azure: naprawa częstych problemów technicznych

Lokalna rozwój funkcji Azure: naprawa częstych problemów technicznych

Dowiedz się, jak narzędzia podstawowe, środowiska wykonawcze języków oraz Azurite muszą być skoordynowane, i otrzymaj praktyczne rozwiązania dotyczące pliku local.settings.json, wyzwalaczy oraz błędów debugowania.

1892 słów

Koniec z walką z emulatorami, uszkodzonymi powiązaniami i tajemniczymi błędami — oto co naprawdę działa

Jeśli zwykły polecenie func start kiedykolwiek pokazało ci ekran wypełniony czerwonym tekstem bez uprzedzenia, nie jesteś jedyną osobą w takiej sytuacji. Azure Functions funkcjonuje doskonale po rozwiązaniu go w chmurze, ale sprawienie, by działał płynnie na twoim własnym laptopie, powoduje, że wielu programistów marnuje całe popołudnie bez żadnego powodu.

To przewodnik pomija wypolerowaną, marketingową wersję pojęcia „rozwój lokalny” i zamiast tego analizuje to, co naprawdę idzie nie tak, przyczyny tych problemów oraz praktyczne rozwiązania, czerpane z trudności, z którymi programiści regularnie się spotykają.

1. Dlaczego rozwój lokalnych Azure Functions wydaje się trudniejszy, niż powinien być

Zapewnianie działania Azure Functions na własnym komputerze to nie tylko wykonywanie kodu. W rzeczywistości tworzysz lokalnie cały środowisko chmurowe: host Functions, powiązania wyzwalaczy, kolejki przechowywania, a czasami także mechanizmy autoryzacji, przy czym w ogóle nie musisz korzystać z samego Azure. Aby to funkcjonowało, na każdym etapie konieczne jest zachowanie synchronizacji trzech odrębnych elementów:

  • Core Tools CLI od Microsoftu, który pełni rolę zamiennika środowiska Functions dostarczanego normalnie przez sam Azure
  • Język programowania i SDK, w którym napisane są twoje funkcje – może to być Node.js, Python, .NET, Java lub PowerShell
  • Azurite, mały emulator, który symuluje Azure Storage, dzięki czemu kolejki, pliki i tabele działają bez konieczności posiadania rzeczywistego konta w chmurze

Jeśli którykolwiek z tych trzech elementów to nieprawidłowa wersja, źle skonfigurowany lub po prostu wyłączony, napotkasz typowe problemy: funkcje, które odmawiają działania, komunikaty „konto przechowywania nie znaleziono” lub serwer, który cicho się wyłącza. Gdy zrozumiesz, jak te trzy elementy są ze sobą powiązane, większość frustracji zniknie.

2. Co naprawdę musisz zainstalować

Zanim dotkniesz jakiegokolwiek kodu funkcji, upewnij się, że masz przygotowane następujące elementy:

  • Azure Functions Core Tools – narzędzie wiersza poleceń, które uruchamia serwer Functions na twoim komputerze
npm install -g azure-functions-core-tools@4 --unsafe-perm true
  • Język w czasie wykonywania zgodny z Twoją docelową wersją Azure (np. Node.js 18/20, Python 3.9–3.11 lub .NET 8)
  • Azurite – emulator, który zastępuje lokalnie usługę Azure Storage
npm install -g azurite
  • VS Code w połączeniu z rozszerzeniem Azure Functions — nie jest to obowiązkowe, ale znacznie ułatwia debugowanie oraz tworzenie szkieletu projektu

Szybka weryfikacja, którą warto przeprowadzić przed dalszym postępem:

func --version
node --version    # or python --version / dotnet --version

Różnica wersji pomiędzy Core Tools a środowiskiem wykonawczym Twojego języka to jeden z najsubtelniejszych i najczęstszych powodów, dla których coś działa poprawnie na jednym komputerze, a zawodzi na innym.

3. Konfiguracja pierwszej lokalnej aplikacji funkcji

Użyj CLI do stworzenia zupełnie nowego projektu:

func init MyFunctionApp --worker-runtime node
cd MyFunctionApp
func new --name HttpTriggerExample --template "HTTP trigger"

Uruchomienie tego skutkuje strukturą folderów zawierającą plik host.json, plik local.settings.json oraz katalog z kodem wyzwalacza. Plik host.json zarządza ustawieniami ogólnymi dla hosta, takimi jak zachowanie logowania, pakiety rozszerzeń i limity czasowe. Plik local.settings.json jest przeznaczony wyłącznie dla twojego komputera i powoduje na początkowym uruchomieniu tyle zamieszania, że wymaga osobnego wyjaśnienia.

4. Plik local.settings.json — co robi i dlaczego wprowadza w błąd

Ten plik przechowuje lokalne zmienne środowiskowe oraz łańcuchy połączeń. Nigdy nie jest wysyłany do Azure; jego jedynym celem jest konfiguracja wyłącznie na poziomie lokalnym.

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "node"
  }
}

Dwa często popełniane błędy tłumaczą większość skarg typu „host nawet się nie uruchamia”:

  1. Zapomnienie o ustawieniu AzureWebJobsStorage. Prawie każda kategoria wyzwalaczy — Timer, Queue, Blob — wymaga połączenia z magazynem, nawet podczas uruchamiania lokalnego. Użycie parametru UseDevelopmentStorage=true skierowuje host na Azurite zamiast na rzeczywisty konto Azure Storage.
  2. Niewłaściwe ustawienie FUNCTIONS_WORKER_RUNTIME. Gdy nie odpowiada on językowi, w którym faktycznie pracujesz (node, python, dotnet, java, powershell), host po prostu nie załaduje twoich funkcji, co zwykle skutkuje niejasnym błędem zamiast wyraźnego komunikatu o niezgodności języka.

5. Azurite: Twój lokalny emulator magazynu (i dlaczego nie możesz go pominąć)

Azurite zastępuje Azure Storage dla aplikacji działających lokalnie, emulując kolejki, obiekty typu blob oraz tabele bezpośrednio na Twoim komputerze. Pomijanie tego kroku jest główną przyczyną błędów StorageException lub odrzucanych połączeń, gdy tylko pojawi się wyzwalacz typu kolejka lub blob.

Zainstaluj go w dedykowanym oknie terminala przed uruchomieniem aplikacji funkcji:

azurite --silent --location ./azurite-data --debug ./azurite-data/debug.log

Jeśli wolisz pracować w VS Code, rozszerzenie Azurite umożliwia uruchomienie emulatora za pomocą pojedynczego wpisu w paletce poleceń, bez konieczności korzystania z osobnego terminala. Niezależnie od wybranej metody, upewnij się, że emulator będzie działał przez całą sesję – bardzo łatwo zapomnieć, że nie jest aktywny, co może skutkować stratą dziesięciu minut na rozwiązywanie problemu z komunikatem „połączenie nieudane”, który w rzeczywistości oznacza jedynie, że emulator nigdy nie został uruchomiony.

6. Uruchamianie i testowanie funkcji wyzwalanych przez HTTP

Gdy Azurite będzie gotowe, uruchom swoje aplikacje funkcyjne:

func start

Twój terminal wyświetli lokalną adres URL każdej funkcji, mniej więcej w takim stylu:

Http Functions:
    HttpTriggerExample: [GET,POST] http://localhost:7071/api/HttpTriggerExample

Możesz do niej uzyskać dostęp za pomocą curl, Postman lub przeglądarki, jeśli jest to żądanie GET:

curl "http://localhost:7071/api/HttpTriggerExample?name=Dev"

Jeśli zamiast odpowiedzi otrzymasz ciszę, sprawdź, czy nie ma kolizji portów – pozostały proces func start lub zupełnie inna instancja mogą już zajmować port 7071. Zakończenie błądzących procesów hostujących funkcje (poszukaj func w Menedżerze zadań lub uruchom pkill -f func na macOS/Linux) zazwyczaj natychmiast rozwiązuje ten problem.

7. Testowanie lokalnych wyzwalaczy nie-HTTP (Timer, Queue, Blob, Service Bus)

Wyzwalacze HTTP to prosty przypadek. Pozostałe wymagają nieco większej przygotowania:

  • Wyzwalacze timera uruchamiają się automatycznie zgodnie z ustalonym harmonogramem CRON zaraz po uruchomieniu hosta, bez konieczności dodawania czegokolwiek dodatkowo. Aby przetestować je wcześniej, możesz tymczasowo dodać do definicji wyzwalacza "RunOnStartup": true, aby ten uruchomił się natychmiast.
  • Wyzwalacze z kolejki wymagają rzeczywistej wiadomości czekającej w kolejce obsługiwaniej przez Azurite. Możesz dodać wiadomość testową za pośrednictwem Azure Storage Explorer, który komunikuje się z Azurite tak samo jak z prawdziwym kontem przechowywania, lub za pomocą rozszerzenia storage w Azure CLI skierowanego do Twojej lokalnej ciągu połączeń.
  • Wyzwalacze typu Blob charakteryzują się opóźnieniami w działaniu lokalnie, ponieważ sprawdzanie nowych plików Blob nie odbywa się natychmiastowo — czekanie kilku minut nie jest rzadkością, chyba że korzystasz z wyzwalaczy Blob opartych na Event Grid. Te ostatnie nie działają dobrze w środowisku lokalnym i zazwyczaj lepiej jest je sprawdzać przy użyciu rzeczywistego, taniego zasobu Azure.
  • Wyzwalacze Service Bus i Event Hub na ogół w ogóle nie mogą być emulowane na twoim komputerze. W takich przypadkach najlepszą opcją jest skierowanie ich na rzeczywisty, niedrogi zasób typu dev-tier w Azure podczas testów lokalnych, poprzez umieszczenie oddzielnego łańcucha połączenia w pliku local.settings.json.
  • To jeden z prawdziwych braków w rozwoju funkcji lokalnych: niektóre typy wyzwalaczy po prostu nie mogą być w pełni odtworzone lokalnie, a traktowanie ich tak, jakby można to było zrobić, tylko marnuje twój czas.

    8. Debugowanie w VS Code

    Tutaj lokalna konfiguracja naprawdę zaczyna się sprawdzać. Po zainstalowaniu rozszerzenia Azure Functions:

    1. Otwórz folder swojego projektu w VS Code.
    2. Umieść punkty przerwania tam, gdzie są potrzebne, wewnątrz kodu wyzwalacza.
    3. Naciśnij F5 — VS Code samodzielnie buduje projekt, uruchamia Azurite, jeśli jest do tego skonfigurowany, startuje host Functions oraz podłącza debuggera, wszystko bez konieczności wykonywania ręcznych kroków.

    Autogenerowane pliki .vscode/launch.json i tasks.json koordynują to wszystko w tle. Jeśli punkty przerwania nie zatrzymują wykonywania kodu, sprawdź, czy ustawienie preLaunchTask w pliku launch.json rzeczywiście ponownie nie buduje twojego kodu przed uruchomieniem hosta — przestarzała wersja budowanego projektu jest subtelnym, ale częstym powodem, dla którego punkty przerwania wydają się być ignorowane.

    9. Powszechne błędy i sposoby ich naprawy

    To konkretna linijka powoduje większe zamieszanie u programistów niż jakikolwiek rzeczywisty defekt w samym środowisku wykonywania Functions. Z powodów bezpieczeństwa plik local.settings.json jest celowo pomijany w pakiecie instalacyjnym ze względu na bezpieczeństwo, co oznacza, że żadne tajemnice ani wartości konfiguracyjne przechowywane tam nie trafią automatycznie razem z aplikacją do Azure — musisz je dodać osobno, albo przez portal Azure, albo za pomocą narzędzi CLI/pipeline.

    10. Uruchamianie Functions lokalnie z użyciem Dockera

    Jeśli twój zespół chce, aby środowiska lokalne i produkcyjne były identyczne — lub jeśli musisz zweryfikować niestandardowy kontener Linuxa — Azure Functions oferuje również rozwiązanie oparte na Dockerze:

    func init MyFunctionApp --worker-runtime node --docker
    cd MyFunctionApp
    docker build -t my-function-app .
    docker run -p 7071:80 -it my-function-app
    

    Taki podejście wiąże się z większym obciążeniem w porównaniu z prostym func start, ale eliminuje całą klasę problemów typu „działa na moim komputerze”, szczególnie w zespołach, które używają spersonalizowanych kontenerów lub wymagają ścisłej spójności na poziomie systemu operacyjnego z tym, co działa w produkcji.

    11. Prawidłowe zarządzanie sekretami i zmiennymi środowiskowymi

    Nie dodawaj pliku local.settings.json do kontroli wersji. Jest on przeznaczony do przechowywania rzeczywistych ciągów połączeń podczas rozwoju, a projekty oparte na szablonach domyślnie wykluczają go z git – sprawdź dokładnie swój plik .gitignore, aby mieć pewność. Pracując w zespole:

    • Dziel się wersją wyczyśczoną, na przykład local.settings.json.example, wypełnioną wartościami tymczasowymi zamiast rzeczywistych sekretów.
  • Gdy przejdziesz poza czyste testy lokalne, polegaj na referencjach do Azure Key Vault dla wszystkich danych wrażliwych.
  • W pipeline’ach CI przekazuj konfigurację za pomocą zmiennych środowiskowych, zamiast dodawać do repozytorium rzeczywisty plik ustawień.
  • 12. Najlepsze praktyki dla sprawnego lokalnego cyklu rozwoju

    • Zaczynaj uruchamianie Azurite przed uruchomieniem hosta Functions — kolejność ma znaczenie, ponieważ niektóre wyzwalacze sprawdzają zasoby przechowywania zaraz po uruchomieniu.
    • Określ dokładnie wersję Core Tools używaną przez twoją zespół, czy to w dokumentacji, czy w skrypcie konfiguracyjnym. Różnice wersji pomiędzy maszynami stanowią cichy, ale realny hamulec produktywności.
    • Uruchamiaj func start --verbose za każdym razem, gdy próbujesz rozwiązać problem z uruchamianiem — domyślny poziom logowania często ukrywa prawdziwą przyczynę.
    • Zrestartuj host za każdym razem, gdy edytujesz plik host.json lub local.settings.json; żaden z tych plików nie jest aktualizowany podczas szybkiego ładowania.
    • Zachowaj dostępny zasób niskiego poziomu w Azure dla typów wyzwalaczy takich jak Service Bus lub Event Grid, które nie mogą być w pełni zreplikowane w lokalnym emulatorze.

    Ostateczne uwagi

    Rozwój lokalny dla Azure Functions nie jest zasadniczo uszkodzony — po prostu składa się z kilku elementów, które muszą być ze sobą skoordynowane, a większość przewodników pomija właśnie te części, które powodują prawdziwe trudności: poprawne emulowanie pamięci przechowywania, niezgodności w środowisku wykonywania zadań oraz granice tego, co lokalne ustawienia mogą symulować, a czego nie. Gdy te trzy kwestie staną się jasne, polecenie func start przestanie wydawać się ryzykowne i stanie się zwykłym, rutynowym poleceniem.

    Jeśli istnieje jeden nawyk, który warto przyjąć z tego wszystkiego, to jest nim następujący: zawsze sprawdzaj, czy Azurite rzeczywiście działa, zanim zaczniesz rozwiązywać jakiekolwiek inne problemy. Ta jedna zaniedbanie potajemnie marnuje więcej czasu niż jakikolwiek prawdziwy błąd w kodzie funkcji.

    Literatura pokrewna

  • Naprawianie błędów w obsłudze błędów Async/Await w kodzie produkcyjnym Node.js — Dowiedz się o pięciu częstych błędach w obsłudze błędów async/await w JavaScript i Node.js, które powodują ciche awarie i sytuacje konkurencyjne, oraz o konkretnych sposobach ich naprawy.
  • Naprawianie błędu braku biblioteki libssl.so.1.1 w Prizmie na Alpine Docker — Dowiedz się, dlaczego silnik zapytań Prizmy zawiesza się na obrazach Docker opartych na Alpine z powodu braku biblioteki libssl, oraz jak trwale to naprawić.