data.mdx.frontmatter.hero_image

Azure Container Instance

2017-08-28 | .NET, Azure, Docker | bd90

Ostatnio, dzięki przygotowaniom do nowego projektu mojej firmy, mam okazję trochę bliżej poznać chmurę Microsoft Azure. W dzisiejszym poście chciałbym Wam przedstawić, jak w prosty sposób uruchomić kontener za pomocą Azure CLI i Azure Container Instance.

Wymagania

Przejście przez ten tutorial wymaga posiadania:

  • Konta Microsoft Azure z podpiętą subskrypcją (może to być subskrypcja okresu próbnego lub też jakaś subskrypcja z rodzaju pay-as-you-go)
  • Konta w serwisie Docker Hub
  • Zainstalowanego Docker
  • W przypadku tworzenia aplikacji .NET Core tak jak w tym tutorialu potrzebujesz dodatkowo .NET Core wersja ^1.1.0 i .NET Core CLI w wersji ^1.0.4

Założenie konta w obu wyżej wymienionych serwisach nie jest jakoś szczególnie trudnym zadaniem, więc nie będę tego opisywał w tym poście :)

Stworzenie aplikacji

Przed uruchomieniem naszego kontenera w chmurze Azure musimy posiadać aplikację, na podstawie której będziemy mogli stworzyć obraz Docker-a. Ja zdecydowałem się na tworzenie aplikacji w technologi .NET Core, ale równie dobrze możecie wybrać jakąś inną technologię i przejść do integracji obrazu z repozytorium serwisu Docker Hub.

Nową aplikację .NET Core tworzymy przy pomocy dotnet CLI:

$ dotnet new webapi -n {nazwa-projektu}

Spowoduje to wygenerowanie się pustego projektu web api.

Dodanie obsługi parametrów z poziomu terminala

Dodatkowo aby ułatwić zarządzanie portem, na którym zostanie odpalona aplikacja musimy dodać paczkę nuget-a Microsoft.Extensions.Configuration.CommandLine za pomocą komendy

$ dotnet add package Microsoft.Extensions.Configuration.CommandLine

Aby korzystać z dobrodziejstw tej paczki musimy zmodyfikować plik Program.cs aby wyglądał następująco

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;

namespace webapi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var config = new ConfigurationBuilder()
                .AddEnvironmentVariables()
                .AddCommandLine(args)
                .Build();

            var host = new WebHostBuilder()
                .UseKestrel()
                .UseConfiguration(config)
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

Ważne jest tutaj wykorzystanie klasy ConfigurationBuilder znajdującej się w namespace-ie Microsoft.Extensions.Configuration, która daje nam możliwość ustawiania parametrów podczas uruchamiania procesu dotnet-a.

Stworzenie pliku Dockerfile

Następnie, w głównym katalogu projektu, musimy stworzyć plik Dockerfile, który będzie odpowiedzialny za stworzenie obrazu Dockera

FROM microsoft/dotnet:runtime
WORKDIR ./dotnetapp
COPY ./bin/Docker .

ENV ASPNETCORE_ENVIRONMENT docker

ENTRYPOINT dotnet webapi.dll --urls="http://*:80"

Analizując zawartość pliku od pierwszej linii:

FROM microsoft/dotnet:runtime -> ustawia nam obraz bazowy (base image), na podstawie, którego będziemy tworzyć swój własny obraz;

WORKDIR ./dotnetapp -> ustawia nam katalog roboczy jako "dotnetapp" (zgodnie z zaleceniami Microsoft-u to właśnie tutaj powinna znajdować się aplikacja);

COPY ./bin/Docker . -> Kopiuje wszystkie pliki znajdujące się w katalogu ./bin/Docker do katalogu ./dotnetapp;

ENV ASPNETCORE_ENVIRONMENT docker -> Ustawia wartość zmiennej środowiskowej ASPNETCORE_ENVIRONMENT na docker. Nie zawsze musi wynosić docker. Równie poprawna będzie np. production czy preproduction. Ważne jest, aby wartość ta informowała nas, w jakim środowisku chcemy odpalić nasz kontener;

ENTRYPOINT dotnet {nazwa-projektu}.dll -> Uruchamia proces .NET-a, standardowo, jeżeli nie skonfigurowaliśmy jakiego nginx-a, który będzie przekierowywał ruch z portu 80 na 5000, to nasza aplikacja będzie dostępna pod portem 5000 na serwerze Kestrel, dlatego dodaje flagę --urls="http://*:80" aby udostępnić aplikację po porcie http.

Po odpowiednim skonfigurowaniu Dockerfile-a wykonujemy na projekcie komendę publish, co spowoduje zbudowanie wszystkich dll-ek (no i w sumie całego projektu):

$ dotnet publish . -c Release -o ./bin/Docker

Ostatnim elementem koniecznym do stworzenia aplikacji będzie zbudowanie obrazu docker-a za pomocą komendy:

$ docker build -t webapi . --no-cache

Czas przejść do udostępnienia obrazu na serwisie Docker Hub.

Wystawianie obrazu na Docker Hub

Mając gotowy kontener naszej aplikacji przechodzimy do upublicznienia go za pomocą repozytorium serwisu Docker Hub.

Po zalogowaniu się do w/w serwisu odnajdujemy, w prawym górnym rogu dashboardu, przycisk "Create Repository +"

Po jego kliknięciu otworzy się formularz dodawania nowego repozytorium

Wystarczy uzupełnić pole "nazwa" nowo tworzonego repozytorium i możemy działać dalej :)

Teraz wracamy do naszego projektu i terminala. Za pomocą Docker CLI logujemy się do odpowiedniego rejestru obrazów docker-a. W tym wypadku jest nim docker hub (ale może to być równie dobrze Azure Container Registry lub Gitlab Container Registry).

Logujemy się przez komendę:

$ docker login

Uzupełniamy nazwę i hasło naszego konta na docker hub (ewentualnie możemy to skrócić do jednej linijki za pomocą parametrów "-p" i "-u")

$ docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD

Dodatkowo, tą wersję, możemy użyć do usług CI aby zrobić automatyczny update obrazu.

Między nami, a prostym i przyjemnym dostępem do obrazu docker - a, stoją już tylko dwie komendy do wpisania:

Najpierw otagujemy nasz obraz tak, aby nazywał się dokładnie tak jak w naszym repozytorium:

$ docker tag webapi bd90/fibon-api

Pozostało wypuścić nasz obraz do rejestru:

$ docker push bd90/fibon-api

Czas na przejście do chmury Azure aby odpalić nasz kontener.

Integracja z Azure Container Service

Rozpoczynając przygodę z usługami Azure potrzebne jest konto w portalu https://portal.azure.com Na dzień dzisiejszy podczas rejestracji otrzymacie 200$ do wykorzystania w 30 dni na dowolne usługi Azure :) Zachęcam do skorzystania, bo to jest kwota, która pozwoli na sporo ciekawych działań.

Wracając do tematu: uruchomienie aplikacji z kontenera Docker-owego za pomocą Azure CLI wymaga aktywowania, na stronie portalu, terminalu. Wykonuje się to klikając w ikonę ">_", która znajduje się w prawym górnym rogu ekranu.

Aplikacja webowa azur-a będzie się łączyć z powłoką chmury, co umożliwi nam zarządzanie kontem bez potrzeby "wyklikiwania" czegokolwiek w trybie okienkowym. Łączenie z terminalem nie powinno zająć więcej niż kilka do kilkunastu sekund. Wynik prezentuje się następująco:

Obecnie wspieraną powłoką jest Bash. W przypadku zainteresowania powłoką PowerShell - jest ona oznaczona jako "coming soon".

Gdy terminal jest aktywny możemy wpisać komendę:

$ az

Zaprezentuje nam to wszystkie możliwości, jakie oferuje. Lista jest dość długa, więc nie zamierzam jej dzisiaj omawiać (może to zrobię w osobnym poście, acz nie wiem czy jest sens ponieważ wpisując np

$ az {komenda} --help

otrzymamy opis co dana komenda robi, wraz z możliwymi podkomendami i flagami).

Skoro mamy już terminal otwarty to teraz musimy stworzyć:

  1. grupę roboczą, gdzie nasz kontener (lub kluster, jeżeli uruchomimy usługę w trybie orchiestracji) zostanie uruchomiony;
  2. nową instancję Azure Container Service, która pobierze obraz kontenera z repozytorium Docker Hub-a i uruchomi go na porcie 80

Tworzenie grupy roboczej

Sporo już za nami, jeszcze kilka "kroków" i dotrzemy do celu. No dobrze, to co należy zrobić, aby utworzyć grupę roboczą? Służy do tego komenda:

$ az group create --name net-test --location eastus

Gdzie "group" to namespace służący do zagregowania wszystkich komend zarządzających grupami roboczymi w jedno miejsce,

"create" jest komendą utworzenia nowej grupy,

"--name" jest nazwą tworzonej grupy roboczej,

"--location" jest lokacją, czyli w jakiej serwowni azure ma powstać grupa robocza. Oznacza to mniej więcej tyle, że to co stworzymy w zakresie owej grupy roboczej będzie się znajdowało w lokacji oznaczonej kodem "eastus". Więcej o tych regionach możecie się dowiedzieć tutaj: https://azure.microsoft.com/pl-pl/regions/

W odpowiedzi od powłoki chmury otrzymamy json-a podobnego do tego poniżej (potwierdza to właściwe wykonanie):

{
  "id": "/subscriptions/{ID}/resourceGroups/net-test",
  "location": "eastus",
  "managedBy": null,
  "name": "net-test",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null
}

Oczywiście, ze względów bezpieczeństwa, usunąłem swój identyfikator subskrybcji :P

Tworzenie instancji Azure Container Service

Przechodzimy do wykorzystania usługi Azure Container Service, aby wystawić naszą aplikację na świat. Ponownie korzystając z Azure CLI wywołujemy polecenie

$ az container create --name hello-azure-container --image bd90/webapi --cpu 1 --memory 1 -g net-test --ip-address public

Gdzie "container" to namespace służący do zarządzania usługami Azure Container Service,

"create" tworzy nową instancje usługi,

"--name" podaje nazwę naszej aplikacji,

"--image" podaję nazwe obrazu docker-a, który znajduje się w docker hubie,

"--cpu" informuje ile rdzeni procesora chcemy posiadać w swoim kontenerze,

"--memory" wyznacza ile GB RAM - u chcemy mieć w kontenerze,

"-g" podaje grupę roboczą, do której zostanie dodana instancja Azure Container Service

"--ip-address" daje możliwość ustawienia, czy chcemy mieć publiczny adres naszego kontenera

Ufff, sporo propertisów tutaj było. Część z nich można pominąć (np. cpu lub memory, wtedy nasz kontener będzie miał default-owe wartości - odpowiednio 1 rdzeń cpu i 1.5 GB ramu).

W odpowiedzi otrzymany w konsoli ponownie json-a, w którym znajdziemy wszystkie informacje na temat stworzonego kontenera.

{
   "containers":[
      {
         "command":null,
         "environmentVariables":[

         ],
         "image":"bd90/webapi",
         "instanceView":null,
         "name":"hello-azure-container",
         "ports":[
            {
               "port":80
            }
         ],
         "resources":{
            "limits":null,
            "requests":{
               "cpu":1.0,
               "memoryInGb":1.0
            }
         },
         "volumeMounts":null
      }
   ],
   "id":"/subscriptions/{ID}/resourceGroups/net-test/providers/Microsoft.ContainerInstance/containerGroups/hello-azure-container",
   "imageRegistryCredentials":null,
   "ipAddress":{
      "ip":"52.168.136.22",
      "ports":[
         {
            "port":80,
            "protocol":"TCP"
         }
      ]
   },
   "location":"eastus",
   "name":"hello-azure-container",
   "osType":"Linux",
   "provisioningState":"Creating",
   "resourceGroup":"net-test",
   "restartPolicy":null,
   "state":null,
   "tags":null,
   "type":"Microsoft.ContainerInstance/containerGroups",
   "volumes":null
}

Tak jak poprzednio usunąłem ID swojej subskrybcji :P

Skoro wszystko zostało wygenerowane poprawnie, możemy przez przeglądarkę wejść na adres IP podany w json-ie zwrotnym (w moim przypadku jest to http://52.168.136.22). Tam właśnie można zobaczyć naszą piękną aplikację dostępną dla całego świata.

Usuwanie instancji Azure Container Service

Dobrze się bawiliście tworząc swoją aplikacje? Teraz niestety przyszedł ten przykry moment, by całą swoją ciężką pracę usunąć. Nie ma co prawda takiego przymusu, jednak pamiętajcie, że zasobność waszego portfela kurczy się za każdym razem, gdy ktoś wejdzie na ten adres IP (nawet przypadkowo).

Zawsze kiedy może ucierpieć mój portfel staram się podchodzić do tematu z rozwagą. Po kilku minutach, gdy zobaczę, że wszystko działa jak planowałem, kasuje instancje Azure Container Service za pomocą komendy

$ az container delete --name hello-azure-container -g net-test

Po chwili aplikacja przestaje być widoczna dla świata.

Podsumowanie

Pora kończyć mój (do tej pory) najdłuższy post na blogu. Krótko podsumowując wiecie już jak:

  • przygotować obraz docker-a dla aplikacji .NET Core
  • za pomocą docker CLI wysłać przygotowany obraz do Docker Hub-a
  • za pomocą azure CLI uruchomić obraz docker-a

Mam nadzieje że miło wam się czytało ten tutorial :)

Życzę wam miłego dnia / nocy / wieczoru czy innej pory dnia w której jesteście, a nawet i całego dnia :) I do następnego!

By Bd90 | 28-08-2017 | .NET, Azure, Docker