Merhaba .NET Core severler.
Daha önce ASP.NET Core serisinin 1. bölümünde bir RESTful API geliştirip, Azure App Services’e deploy işlemini gerçekleştirmiştik. Şimdi bu 3. bölümünde ise, daha önce geliştirmiş olduğumuz bu RESTful API‘ı, nasıl containerize edebileceğimizi ve ardından Azure Container Service ile Kubernetes‘e nasıl deploy edebiliriz gibi konulara, çalışmakta olduğum firmada .NET Core transformation sürecinde elde edebildiğim tecrübeler doğrultusunda değinmeye çalışacağım.
Konu başlıkları sırasıyla:
NOT: Bu makale içerisinde detaylı olarak Docker nedir, neden ihtiyaç duyarız gibi konulara değinmeyeceğim. Bu makale için Docker hakkında biraz bilgiye sahip olmanız gerekmektedir.
Bildiğimiz gibi container image’i lightweight, stand-alone ve her an çalışmaya hazır application parçalarıdır. Container’lar ile uygulamalarımız, aynı infrastructure altında birbirlerinden izole bir şekilde çalışabilmektedir. Böylece development ve staging ortam farklılıkları birbirlerinden ayrılabileceği için, oluşabilecek conflict’lerin azaltılmasına da yardımcı olmaktadır.
Containerizing işlemi için “ASP.NET Core Serisi 01: Dapper ile RESTful API Tasarlama ve Azure App Services’e Deploy” isimli makale içerisinde geliştirdiğimiz örnek projeyi kullanacağız. Örnek projeyi GitHub üzerinden download edelim ve masaüstüne unzip işlemini gerçekleştirelim.
NOT: Gerçekleştireceğim işlemler için, Windows platformu üzerinde DockerToolbox kullanmaktayım.
Bu aşamada ilk olarak, bir image build edebilmemiz için unzip yaptığımız projenin root dizininde, aşağıdaki gibi bir Dockerfile oluşturalım.
FROM microsoft/aspnetcore:1.1 ENTRYPOINT ["dotnet", "aspnetcore-rest-api-with-dapper.dll"] ARG source=. WORKDIR /app EXPOSE 5000 COPY $source .
Dockerfile içerisinde base olarak offical compiled ASP.NET Core image’ini kullanacağımızı ve “5000” port’u üzerinden ise expose edeceğimizi tanımladık. Entrypoint olarak ise, projeyi publish ettikten sonra elde edecek olduğumuz “aspnetcore-rest-api-with-dapper.dll” dosyasını belirttik. Şimdi projenin root dizinine herhangi bir command tool’u ile girelim ve projeyi aşağıdaki gibi build edelim.
dotnet build
NOT: “dotnet restore” komutu ile package’ları restore etmeyi unutmayın.
Build işleminin başarıyla gerçekleşmesinden sonra, aşağıdaki komutu kullanarak projeyi publish edelim.
dotnet publish -c release
Artık, publish işleminin ardından Dockerfile’ı kullanarak bir image build edebiliriz. Image build edebilmemiz için aşağıdaki komutu çalıştırmamız yeterli olacaktır.
docker build bin\Release\netcoreapp1.1\publish -t aspnetcorerestapionlinux
Publish sonrası elde ettiğimiz “bin\Release\netcoreapp1.1\publish” path’ini kullanarak, “aspnetcorerestapionlinux” isminde bir image build ettik. Aşağıdaki komutu kullanarak, oluşturulan image’leri görebiliriz.
docker images
Yukarıdaki resimde, “aspnetcorerestapionlinux” ismi ile image’in oluştuğunu görebiliriz.
Şimdi, yaptığımız işlemlerin doğru gittiğinden emin olabilmek için oluşturmuş olduğumuz image’i, bir container içerisinde aşağıdaki gibi çalıştıralım.
docker run -d -p 5000:5000 aspnetcorerestapionlinux
Oluşturmuş olduğumuz “aspnetcorerestapionlinux” image’ini, bir container içerisinde “5000” port’u üzerinden expose ederek çalıştırdık. Şimdi browser üzerinden “http://192.168.99.100:5000/swagger” URL’ine girelim ve projenin çalıştığından emin olalım.
Tadaa! Oluşturmuş olduğumuz image, container içerisinde başarılı bir şekilde çalışmaktadır. Buraya kadar olan kısımda, ASP.NET Core ile geliştirmiş olduğumuz RESTful bir API’ın containerizing işlemini tamamladık.
Şimdi ise oluşturmuş olduğumuz bu “aspnetcorerestapionlinux” image’ini, Docker Hub üzerine push edeceğiz. Bu sayede Azure Container Service içerisinde bu image’i pull ederek, Kubernetes cluster’ı içerisinde çalıştıracağız. Öncelikle push işlemini gerçekleştirebilmemiz için, Docker Hub account’ına sahip olmamız gerekmektedir. Eğer account’a sahip değilsek, buradan oluşturabiliriz.
İlk olarak oluşturmuş olduğumuz image’e, aşağıdaki gibi bir tag eklememiz gerekmektedir.
docker tag aspnetcorerestapionlinux gokgokalp/aspnetcorerestapionlinux
NOT: “gokgokalp” olan kısmı, kendi Docker Hub kullanıcı adınız ile değiştirmeyi unutmayın.
Şimdi ise Docker Hub üzerine login olmamız gerekmektedir.
docker login
Login olma işlemi başarıyla gerçekleştikten sonra aşağıdaki gibi oluşturmuş olduğumuz image’i, Docker Hub üzerine push edelim.
docker push gokgokalp/aspnetcorerestapionlinux
Azure Container Service, containerize hale getirdiğimiz uygulamalar için basit ve hızlı bir şekilde sanal makineler oluşturabilmemizi, yapılandırabilmemizi ve cluster yapımızı yönetebilmemizi kolaylaştırmak için çözümler sunmaktadır. Microsoft‘un bir kaç ay önce Kubernetes container orchestration service’ini de tamamen available hale getirmesiyle beraber, efficiently bir şekilde containerize edilmiş uygulamalarımızı deploy ve on the fly olarak scale edebilmek kolay bir hal almıştır.
Şimdi Azure Container Service‘ini kullanarak, basic bir şekilde Kubernetes cluster’ı oluşturacağız. Cluster oluşturabilmek için öncelikle aşağıdaki maddelere ihtiyaç duymaktayız:
Haydi ozaman başlayalım.
2.1 SSH RSA Public Key Oluşturma
Windows üzerinde oluşturabilmek için, Git for Windows‘un kurulu olması gerekmektedir. Eğer kurulu değilse: https://git-for-windows.github.io/
Git Bash‘i açalım ve aşağıdaki komut satırı ile “openssl.exe” yi kullanarak, “myPrivateKey” ve “myCert” certificate’ini oluşturalım.
openssl.exe req -x509 -nodes -days 365 -newkey rsa:2048 \-keyout myPrivateKey.key -out myCert.pem
Bu işlem sırasında size “Country Name, State, E-mail, Organization Name” gibi bir kaç bilgi soracaktır. Gerekli bilgileri girdikten sonra, “myPrivateKey” ve “myCert” certificate’i, ilgili path altında oluşacaktır.
Şimdi ise aşağıdaki komutu kullanarak, oluşturmuş olduğumuz private key ile “myPublicKey” adında bir public key oluşturacağız.
openssl.exe rsa -pubout -in myPrivateKey.key -out myPublicKey.key
PuTTY SSH client’ını kullanabilmemiz için, ek bir key daha oluşturmamız gerekmektedir. Bunun için:ddddddd
openssl rsa -in ./myPrivateKey.key -out myPrivateKey_rsa
Bununla birlikte “myPrivateKey_rsa” isminde bir private key daha elde ettik.
Şimdi oluşturmuş olduğumuz bu “myPrivateKey_rsa” key’ini kullanarak, Azure portal üzerinde Linux VM yaratırken kullanacağımız public key’i oluşturacağız. Bunun için öncelikle buradan, PuTTY ‘i indirelim ve kuralım. Kurulum işleminin ardından “PuTTYgen” uygulamalasını çalıştıralım ve “Load” kısmından oluşturmuş olduğumuz “myPrivateKey_rsa” key’ini seçelim.
Yukarıdaki resimde gördüğümüz gibi “ssh-rsa” prefix’i ile başlayan bir public key daha elde etmiş olduk. Makalenin ilerleyen bölümünde kullanabilmek için, bu key bilgisini de kaydedelim.
2.2 Service Principal Client ID and Secret Oluşturma
Bu noktada Azure AD (Active Directory) service principal oluşturma işlemini, portal üzerindeki terminal’den gerçekleştireceğiz. Azure portal‘a login olduktan sonra, sağ üst menüde bulunan “Cloud Shell” i açalım.
“Cloud Shell” i açtıktan sonra, öncelikle aşağıdaki komut ile “aspnetcore-test-restapi” adında bir resource group oluşturalım.
az group create -n "aspnetcore-test-restapi" -l "westeurope"
Resource group’u oluşturmanın ardından, aşağıdaki komut içerisindeki “mySubscriptionID” bölümüne kendi subscription ID bilgimizi girelim çalıştıralım.
az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/mySubscriptionID/resourceGroups/aspnetcore-test-restapi"
Bu işlemin ardından aşağıdaki gibi bir output elde edeceğiz.
Buradaki “appId” yi principal client ID olarak, “password” u ise secret olarak kullanacağız.
2.3 Kubernetes Cluster’ı Oluşturma
Artık bir Kubernetes cluster’ına sahibiz.
2.4 Kubernetes Cluster’ına Bağlanmak
Local makinemizden Kubernetes cluster’ına bağlanabilmemiz için, “kubectl” i kurmamız gerekmektedir. Bunun için buradan, ilgili işletim sistemimize göre Azure CLI‘ı indirelim ve kuralım. Kurulum işleminin ardından, command tool açalım ve aşağıdaki komut ile “kubectl” in kurulumunu gerçekleştirelim.
az acs kubernetes install-cli
Şimdi ise Kubernetes cluster configuration bilgilerini download etmemiz gerekmektedir. Öncelikle aşağıdaki komut ile, portal bilgilerimizi kullanarak Azure üzerine login olalım.
az login
Login olduktan sonra, aşağıdaki komut üzerinde gerekli değişiklikleri yapalım ve çalıştıralım.
az acs kubernetes get-credentials --resource-group=aspnetcore-test-restapi --name=aspnetcore-container-service --ssh-key-file=C:\Users\GOKGOKALP\myPrivateKey_rsa
Cluster configuration bilgilerinin başarıyla download edildiğinden emin olabilmek için, aşağıdaki komutu da çalıştıralım ve node listesini görelim.
kubectl get nodes
2.5 Kubernetes Cluster’ı İçerisindeki Bir Pod’da ASP.NET Core RESTful API’ını Çalıştırma
Dokümantasyonunda da olduğu gibi, Pod’u tek bir cümle ile tanımlamak gerekirse eğer:
Kubernetes içerisindeki küçük, deployable computing birimleridir.
Pod, bir veya birden fazla application container’larını içerebilmektedir. Şimdi Kubernetes cluster’ı içerisinde bir pod oluşturabilmemiz için, projenin root klasörü altında “aspnetcore-rest-api-pod.yaml” adında aşağıdaki gibi bir YAML file’ı tanımlayalım.
apiVersion: v1 kind: Pod metadata: name: aspnetcore-restapi labels: app: aspnetcore-restapi spec: containers: - name: aspnetcore-container image: "gokgokalp/aspnetcorerestapionlinux" ports: - containerPort: 5000
Yukarıda tanımlamış olduğumuz YAML file’ı içerisindeki “image” kısmına, Docker repo’ya push yaptığımız image’i belirtiyoruz. Hatırlarsak API‘ı container içerisinde de “5000” port’u üzerinden expose etmiştik.
Şimdi aşağıdaki komutu, projenin root dizini altında command tool’u üzerinden çalıştıralım.
kubectl create -f aspnetcore-rest-api-pod.yaml
Gördüğümüz gibi pod’u başarıyla oluşturduk. Şimdi aşağıdaki komut ile oluşturmuş olduğumuz pod’a bir bakalım.,
kubectl get pods
Yukarıdaki resimde oluşturmuş olduğumuz “aspnetcore-restapi” pod’unun, “running” statüs’ünde çalıştığını görebiliriz. Bu pod içerisinde, demo projemiz olan ASP.NET Core RESTful API’ı çalışmaktadır. Artık yapmamız gereken tek şey, bu pod’u dışarıya Kubernetes Service ile expose etmek olacaktır.
2.6 Kubernetes Service ile Pod’u Expose Etmek
Kubernetes Service, logical pod’lar seti tanımlayabilmek ve policy’ler oluşturabilmek için olan bir abstraction’dır. Detaylı bilgiye ise, buradan ulaşabilirsiniz.
Şimdi Kubernetes Service için “aspnetcore-rest-api-ks.yaml” adında bir YAML file daha oluşturalım.
apiVersion: v1 kind: Service metadata: name: aspnetcore-restapi-service labels: app: aspnetcore-restapi-service spec: selector: app: aspnetcore-restapi ports: - port: 80 targetPort: 5000 protocol: TCP type: LoadBalancer
Yukarıdaki YAML file’ı ile “aspnetcore-restapi-service” adında, target TCP port’u “5000” olan bir Kubernetes Service‘i oluşturacağız. Ayrıca “selector” kısmında bulunan “app: aspnetcore-restapi” değerinin, oluşturmuş olduğumuz pod’un label’ı ile aynı olduğuna dikkat edelim. Şimdi service’i oluşturabilmek için, aşağıdaki komutu çalıştıralım.
kubectl create -f aspnetcore-rest-api-ks.yaml
Service oluşturma işleminin ardından, aşağıdaki komut ile de service listesine bir bakalım.
kubectl get services
Yukarıdaki resme dikkat edersek “aspnetcore-restapi-service” adlı service’in, otomatik olarak assign edilen “52.232.119.105” external IP‘si ile expose olduğunu görebiliriz.
NOT: IP assign edilme işlemi, bir kaç dakika sürebilir.
Browser üzerinden eriştiğimizde ise, aşağıdaki gibi Swagger UI karşımıza gelecektir.
Şimdi geriye sadece Deployment Controller‘ı oluşturmak kaldı.
2.7 Kubernetes Deployment Controller’ı Oluşturma ve Expose Etme
Bu noktaya kadar Azure Container Service‘i kullanarak bir Kubernetes cluster’ı oluşturduk ve içerisinde RESTful API’ımızın container’ının çalıştığı bir pod tanımladık. Daha sonra bir Kubernetes Service‘i tanımlayarak, Load Balancer ile “80” port’u üzerinden dışarıya expose ettik.
Buraya kadar her şey yolunda. Eğer pod’larla ilgili buradan “Durability of pods (or lack thereof)” başlığını okursak: pod’ların durable entities olmadıklarını ve scheduling failures, node failures gibi durumlarda yeniden kullanılamadıklarını görebiliriz. Buda demek oluyor ki ilgili pod öldüğünde, ilgili service ulaşılamaz olacaktır. Production ready olup bu gibi durumlara maruz kalmamamız için, Kubernetes Controller‘ları kullanmalıyız. Kubernetes içerisinde, Deployments ve Replication Controller gibi controller’lar mevcuttur. Biz burada Replication Controller‘ın yeni nesli olan Deployments‘ı kullanacağız. Deployments genel olarak self-healing, rollout management, scaling gibi önemli işlemleri sağlamaktadır.
Şimdi, aşağıdaki gibi “aspnetcore-rest-api-deployment.yaml” adında bir deployment YAML file’ı tanımlayalım.
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: aspnetcore-restapi-deployment spec: replicas: 3 selector: matchLabels: app: aspnetcore-restapi template: metadata: labels: app: aspnetcore-restapi spec: containers: - name: aspnetcore-container image: "gokgokalp/aspnetcorerestapionlinux" ports: - containerPort: 5000
Dikkat edersek “replicas” bölümünü “3” olarak tanımladık ve ardından “containers” bölümü altında ise tıpkı pod oluşturma YAML file’ında olduğu gibi, ilgili image bilgilerini ve port bilgilerini belirttik. Artık bu template doğrultusunda Deployment, scheduling failures ve node failures gibi durumlar karşısında bizim için “3” adet replika’nın var olduğundan emin olacaktır.
Aşağıdaki komut ile deployment’ı oluşturmadan önce, daha önce oluşturmuş olduğumuz “aspnetcore-rest-api-pod.yaml” pod’unu ve “aspnetcore-rest-api-ks.yaml” service’ini silelim.
kubectl delete -f aspnetcore-rest-api-pod.yaml
kubectl delete -f aspnetcore-rest-api-ks.yaml
Şimdi deployment’ı oluşturabiliriz. Bunun için:
kubectl create -f aspnetcore-rest-api-deployment.yaml
ve deployment oluşmuş durumda. Hemen oluşan pod’lara bir bakalım:
kubectl get pods
Yukarıda gördüğümüz gibi deployment, 3 adet “running” statüs’ünde pod oluşturmuş durumdadır. Ayrıca deployment ile daha fazla yükü otomatik olarak scale edebilmekte mümkündür. Bunun için detaylı bilgiye, buradan erişebilirsiniz.
Şimdi ise geriye kalan tek şey, bir service oluşturarak bu 3 pod’u dışarıya tekrardan expose etmek olacaktır. Bu işlem için daha önce kullanmış olduğumuz “aspnetcore-rest-api-ks.yaml” file’ını tekrardan kullanacağız. Hatırlarsak deployment controller ile pod’ları oluşturabilmek için service’i silmiştik.
apiVersion: v1 kind: Service metadata: name: aspnetcore-restapi-service labels: app: aspnetcore-restapi-service spec: selector: app: aspnetcore-restapi ports: - port: 80 targetPort: 5000 protocol: TCP type: LoadBalancer
Bu service “app: aspnetcore-restapi” label’ına sahip olan 3 pod’u, “80” port’u üzerinden dışarıya expose edecektir.
kubectl create -f aspnetcore-rest-api-ks.yaml
Sonunda hazırız. 🙂 Yukarıdaki resimler görebildiğimiz gibi bu 3 pod’u dışarıya bir Load Balancer service’i ile expose ettik. Hangi endpoint’leri expose ettiğine bakmak gerekirse eğer, aşağıdaki komutu çalıştırmamız yeterli olacaktır.
kubectl describe services aspnetcore-restapi-service
Yukarıdaki komutu çalıştırmanın ardından Load Balancer service’inin “10.244.1.7:5000“, “10.244.1.8:5000” ve “10.244.1.9:5000” pod’larını “http://52.232.119.105” URL’i üzerinden expose ettiğini görebiliriz.
Biraz uzun bir makale oldu ama gerçekten zevkli ve önemli bir konu. Şuan çalışmakta olduğum firmada, bazı projeler için .NET Core transformation’ı ile birlikte Containerizing ve Kubernetes konuları üzerinde yoğun bir şekilde çalışmaktayım. Bu makale umarım ihtiyacı olanlara yardımcı olur.
Bir sonraki .NET Core makale serisinde ise, Jenkins kullanarak CI pipeline süreçlerine nasıl dahil edebiliriz konusunu ele almaya çalışacağım.
Takip de kalın!
https://github.com/GokGokalp/containerizing-and-kubernetes-sample-files
https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/container-service/container-service-tutorial-kubernetes-prepare-app.md
https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/container-service/container-service-tutorial-kubernetes-prepare-acr.md
https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/container-service/container-service-tutorial-kubernetes-deploy-cluster.md
https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/container-service/container-service-tutorial-kubernetes-deploy-application.md
https://www.docker.com/what-container
https://docs.microsoft.com/tr-tr/azure/container-service/container-service-intro
https://kubernetes.io/docs/concepts/overview/what-is-kubernetes
https://docs.microsoft.com/en-us/azure/container-service/container-service-deployment
https://docs.microsoft.com/en-us/azure/virtual-machines/linux/ssh-from-windows
https://docs.microsoft.com/en-us/azure/container-service/container-service-kubernetes-walkthrough
http://www.c-sharpcorner.com/blogs/containerizing-a-net-core-application-using-docker-acs-and-kubernetespart-4
https://kubernetes.io/docs/concepts/services-networking/service/
{:tr} Makalenin ilk bölümünde, Software Supply Chain güvenliğinin öneminden ve containerized uygulamaların güvenlik risklerini azaltabilmek…
{:tr}Bildiğimiz gibi modern yazılım geliştirme ortamında containerization'ın benimsenmesi, uygulamaların oluşturulma ve dağıtılma şekillerini oldukça değiştirdi.…
{:tr}Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı…
{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…
{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…
{:tr}Bir önceki makale serisinde Dapr projesinden ve faydalarından bahsedip, local ortamda self-hosted mode olarak .NET…
View Comments
süper bir konu kalemine sağlık
Çok teşekkür ederim.