Deploy Node.js ด้วย Azure App Service Plan, Azure Container Registry และ GitHub Action
สวัสดีผู้อ่านทุกท่านนะครับ ก่อนหน้านี้ผมมีโอกาสได้สรุปแนวทางการ Deploy .NET Web App ขึ้นไปที่ Azure App Service และสามารถสั่งให้เกิดการ Deploy ได้แบบอัฒโนมัติถ้ามีการแก้ไขเกิดขึ้นที่ GitHub ด้วยความช่วยเหลือของ GitHub Action ครับ
ท่านใดที่สนใจสามารถศึกษาวิธีได้ที่ Video นี้นะครับ หลักๆอยากให้ดูตรงนี้ก่อนเพื่อเข้าใจคำศัพท์ต่างๆที่เกี่ยวข้องกับการใช้งาน Azure App Service ครับผม
ในระหว่างการสอน มีคำถามนึงที่น่าสนใจมาก ว่านอกจากจะ Deploy .NET Web App แล้ว อยากขอให้ทดลอง Deploy Node.js ด้วยและอยากให้ Deploy แบบ Container ครับ ในบทความนี้เลยจะมาพาผู้อ่านทดลองกันนะครับ ลุยยย
สิ่งที่ต้องเตรียมทั้งหมด
Docker Desktop
เพื่อใช้รัน Docker Cli นะฮะGitHub Repository
ที่เก็บ Node.js Web App ของเรา รวมไปถึง Dockerfile เพื่อสร้าง Docker Image นะครับ และเราจะสร้าง GitHub Action Pipeline ใน Repository นี้เช่นกันครับAzure Container Registry
เพื่อใช้ในการเก็บ Docker Image ของเราและนำไป Deploy ต่อที่ Azure App Service ครับAzure App Service Linux
เป็นที่ที่เราจะนำ Docker Image มา Deploy เพื่อเป็น Web App หลักของระบบนะครับ
แอบสปอยว่ามีตัวช่วยสร้าง Pipeline ให้อัตโนมัติเลยครับ รวมถึง Secret Key ต่างๆที่ต้องใช้ในการเชื่อม GitHub และ Azure ด้วยครับ สะดวกสุดๆไปเลยพี่น้อง
Docker Desktop
ใครที่ยังไม่เคยติดตั้ง Docker Desktop รวมถึงการใช้คำสั่งเบื้องต้นต่างๆ ผมอยากขอแนะนำลองทำตามบทความนี้ได้นะครับสำหรับสาย Windows โดยใช้ร่วมกับ WSL2 ครับผม
ส่วนถ้าใครใช้ Mac ก็ลองตามนี้นะครับ
GitHub Repository
ให้เราทำการ Fork Repository ของผมได้เลยนะครับ เป็นตัวอย่าง React App ง่ายๆที่เตรียม Dockerfile มาพร้อมเลย มาลองรันในเครื่องกัน
เมื่อได้ Code ในเครื่องแล้วจะเห็นหน้าตาประมาณนี้นะครับ โดยเป็น Project ที่สร้างมาจาก Create React App
แบบเริ่มต้นเลยไม่ได้ปรับแต่งอะไรครับผม
เรามาลองรัน Web App กันง่ายๆก่อนด้วยคำสั่ง
npm start
เสร็จแล้ว Click Link http://localhost:3000/ เพื่อดูผลนะครับ
ทีนี้เรามาลองสร้าง Docker Image กันนะครับ เนื่องจากเรามี Docker File อยู่แล้ว ให้ลองรันคำสั่งข้างล่างเพื่อสร้าง Docker Image ที่ชื่อ react-azure
กันนะครับ
docker build -t react-azure .
เสร็จแล้วให้ลองเปิด Docker Desktop จะพบว่ามี Docker Image ที่เราต้องการขึ้นมาแล้วนะครับ
ถ้าท่านใดอยากทราบวิธีการสร้าง Docker File โดยละเอียด สามารถติดตามได้จากบทความนี้นะครับ ปูพื้นฐาน Docker, Docker Compose แน่นๆเยย เย้
ให้ลองรันคำสั่งข้างล่างนี้เพื่อรัน Web App ของเราอีกครั้ง แต่เปลี่ยนจากการรันด้วย Node Runtime มาเป็น Docker Runtime แทนนะครับ เพื่อทดสอบว่า Docker Image ของเราทำงานได้ถูกต้องจริงๆ
docker run -p 8000:80 --name react-azure react-azure
เหตุผลที่ต้อง Map Docker Port 80 เพราะว่าใน Dockerfile เราใช้วิธี Build แบบ Multi Stage ครับผม เพื่อลดขนาดของ Docker ลง โดยนำ Nginx มาช่วย และมีการ Configure Port 80 ไว้ครับ
เมื่อรันเสร็จแล้วให้เราเปิด Docker Desktop อีกทีนะครับ จะพบว่ามี Container ขึ้นมาแว้ว แปลว่า Web App ของเราพร้อมใช้งานแล้วครับ
ที่เด็ดที่ยก Docker Desktop ขึ้นมาคือ การดู log ที่สะดวกมากๆ, ตรวจเช็คย้อนหลังได้ว่าเรา Mount Port, Environment อะไรไว้บ้าง รวมไปถึงการใช้ Terminal ใน Docker Container ของเราอีกที เผื่ออยากเข้าไปตรวจสอบว่า Docker ของเราทำงานได้สมบูรณ์ไหมนะครับ
ในเมื่อเรามี docker-compose.yaml
ให้ใช้งานได้สะดวกๆแล้ว เลยอยากแนะนำวิธีรัน Web App อีกแบบที่ Code สั้นกว่าเยอะเลยฮะ ไม่ต้องใส่ Options เยอะๆแล้ว
ก่อนรันให้ปิด Docker Container ก่อนหน้านี้ด้วยนะครับ
docker compose up
กลับไปดูผลลัพธ์ที่ Docker Desktop กันอีกทีนะครับ ก็จะพบว่า Web App เราพร้อมใช้ในอีกรูปแบบนึงแล้วครับผม เท่านี้การเตรียมการ Docker Image
ปัญหาเรื่อง Platform ที่เจอเฉพาะบน Mac M1/M2
เนื่องจากในช่วงที่ผมทดลองนำ Docker Image ที่ Build จากเครื่องของผม ที่เป็น Macbook Air M2 แล้วพบปัญหาว่า Image บางตัวรันบน Azure ไม่ได้ครับผม
เพื่อแก้ไขปัญหาในจุดนี้ ผมจึงทำการ Build Image โดยระบุ Platform ไปเลยว่าสำหรับ Linux ครับผม ซึ่งมีข้อดีคือ Image นี้ไม่มีปัญหาใน Azure ละ แต่รันในเครื่องผมไม่ได้นะ ฮ่าๆ เลยจะเป็นเศร้าๆนิดนึง เวลาอยากทดลองในเครื่องก็ Build Image นึง เวลาอยากเอาขึ้น Azure ก็ Build อีก Image นึงแทนชั่วคราวไปก่อนนะครับ
กราบขอโทษที่ยังหาวิธีที่ดีกว่านี้ไม่ได้ครับ T__T
แอบแนะนำบทความคุณสมเกียรติครับผม สุดยอด
ขั้นตอนการ Build จะเปลี่ยนเป็นใช้คำสั่งดังนี้นะครับ
docker buildx build --platform linux/amd64 -t {image} .
เมื่อ Build ได้แล้วก็มาลองดูผลลัพธ์กันจะเห็นว่าขึ้นแดงเลยว่าเป็น AMD64 ไม่ใช่ Arm แหล่ว
เผื่อท่านใดอยากจะ Build Docker แบบ Multi-Platforms ลองคำสั่งตามนี้ได้นะครับ (เครื่องผมรันไม่ได้ ฮะๆ ได้ทีละ Platform)
docker buildx build --platform linux/arm64,darwin/amd64 -t {image} .
Azure Container Registry (ACR)
เป็นพื้นที่เก็บ Docker Images คล้ายๆ Docker Hub นะครับ แต่จะเป็นบริการบน Microsoft Azure ถ้าใครสนใจศึกษารายละเอียดเพิ่มเติมสามารถดูได้ที่นี้ครับ
เพื่อความสะดวกผมขอย่อคำว่า Azure Container Registry เหลือแค่ ACR นะครับ
ในขั้นตอนการสร้าง ACR ให้เราทำขั้นตอนข้างล่างเพื่อ Pin Menu ให้เข้าถึงได้สะดวกๆก่อนนะครับ
เสร็จแล้วให้กดที่ Menu Container registries
ทางซ้ายมือแล้วกด Create เพื่อสร้าง ACR ได้เลยครับ
ใน Tab Basics ให้เราทำการระบุชื่อ Registry/Location ที่ต้องการ และเลือก Pricing Plan Basic ที่จะมีค่าใช้บริการต่อเดือนประมาณ 7 USD ครับ (ถ้าท่านใดต้องการเลือก Package อื่น สามารถศึกษาได้ที่นี้นะครับ Pricing — Container Registry)
ส่วนข้อมูลอื่นให้คงไว้ตาค่าเริ่มต้นแล้วกดสร้างได้เลยครับ
เมื่อสร้างเสร็จแล้วให้เราทำการเพิ่ม Admin user
เพื่อใช้ในการ Login เพื่อ Push Docker Image จากเครื่องเราเข้าไปเก็บที่ ACR นะครับ
Microsoft แนะนำให้ใช้ Admin user เฉพาะกรณีของการทดสอบเบื้องต้นเท่านั้นนะครับ แนวทางที่ปลอดภัยกว่าคือการใช้ Tokens เพื่อกำหนดสิทธิได้แยกตามการใช้งานนะครับ
เมื่อได้ User พร้อมแล้ว ให้เรากลับมาที่ Terminal ของเราและทำการ Login ด้วย Docker Login CLI นะครับ ระบุ User / Password ตาม Access Key ข้างบนได้เยย
docker login {ACR Path}
ตัวอย่าง
docker login ttssreactazure.azurecr.io
เสร็จแล้วให้ทำการ Tag Docker Image เพื่อเตรียมที่จะ Push ขึ้น ACR ครับผม
docker tag {image} {ACR Path}/{image}
ตัวอย่าง
docker tag react-azure ttssreactazure.azurecr.io/react-azure
docker tag react-azure-amd ttssreactazure.azurecr.io/react-azure-amd
ขั้นตอนสุดท้ายแล้วครับ สั่ง Push ขึ้น ACR ได้เลย
docker push {ACR Path}/{image}
ตัวอย่าง
docker push ttssreactazure.azurecr.io/react-azure
docker push ttssreactazure.azurecr.io/react-azure-amd
ดูผลลัพธ์ใน ACR จะพบว่ามี Docker Image ใน Repository มาแล้วนะครับ
ถ้าใครเจอปัญหาเหมือนผมนะครับคือไม่สามารถดู Repository ได้ ให้ทำการเพิ่ม Reader Role ใน IAM นะครับ จบปิ้งงงง
กลับไปดู Repository คราวนี้มาแน่นอนครับ
Image ก็พร้อมบน ACR แล้ว เดี๋ยวเราไปลองติดตั้งบน Azure App Service Plan กันฮะ
Azure App Service Plan Linux — Container
เป็นบริการ Web Server แบบ PaaS ที่ผมชื่นชอบมานานครับ เพราะตอบโจทย์การทำงานหลายรูปแบบ เราเลือกได้ทั้ง Windows / Linux รวมไปถึง Runtime ต่างๆทั้งแบบ Code ที่มี Runtime ติดตั้งมาให้ และแบบ Docker ที่เราเอา Image ไปวางได้เลย แล้วรันแบบ Container ครับผม ถ้าใครสนใจรายละเอียดเพิ่มเติม ผมแนะนำ Video ข้างบนน้า…
เรามาเริ่มสร้าง App Service Plan กัน โดยเริ่มจากการเพิ่มเมนูให้อยู่ทางซ้ายมือก่อนเพื่อสะดวกในการค้นหานะครับ
ให้เรากด Menu App Service plans
เพื่อทำการสร้าง Web Server Linux กันตามนี้นะครับ เน้นที่
- Operating System = Linux เพราะเราต้องใช้ Docker Container Linux
- Pricing Plan = Basic จะได้ประหยัดค่าใช้จ่ายครับ (ราคา 14.60 USD ต่อเดือน)
ส่วนข้อมูลอื่นๆใช้เป็นค่าเริ่มต้นได้หมดเลยครับ
กด Review + Create เพื่อสร้าง App Service Plan Linux -> จบปิ๊ง
ขั้นตอนถัดมาให้เรากด Menu App Services
เพื่อทำการสร้าง Web App กันซะทีนะครับ ฮ่า ลากยาวมาหลายขั้นตอน โดยเน้นที่
- Publish = Docker Container
- Operating System = Linux
- Region = เลือกให้ตรงกับที่เราสร้าง App Service Plan
- Linux Plan = เลือก App Service Plan ที่เราพึ่งสร้างได้เลยครับ
ในขั้นตอนถัดมานี้สำคัญเลยครับ คือการเลือก Docker Image จาก ACR ของเราเพื่อติดตั้งลงใน Web นี้นะคร้าบ
ข้อมูลอื่นๆให้คงไว้ที่ค่าเริ่มต้นนะครับและกด Create ได้เลย เราจะได้หน้าตาประมาณรนี้เมื่อสร้างเสร็จแล้วครับ
ให้ลองกดจิ้มที่ Default domain
เราจะพบกับหน้าตา Web ที่ค้างไม่มาสักทีครับฮ่าๆๆๆ มีอะไรที่ทำทีเดียวผ่านไหมนะ
หยอกๆๆๆๆ จริงๆตัวระบบกำลัง Pull Image ลงมาครับ เลยต้องให้เวลาพี่แกหน่อย ยิ่ง Image ใหญ่ก็ยิ่งใช้เวลานานครับ ต้องไปปรับขนาดให้ Image ลดลงในจุดที่เหมาะสมน้า
หลังจากรอคอยสักพัก ในที่สุดดดด
ถ้าใครอยากดู Log แล้วรู็สึกว่าใน Deployment Center มันดูไม่สะใจ ผมแนะนำให้เปิด Application Log ตามขั้นตอนนี้นะครับ
ขั้นตอนแรกให้ไปที่ App Service logs -> กด File System -> เลือก 1 Day Retention Period
เสร็จแล้วเลือก Advanced Tools -> Go -> Log stream
คราวนี้ดูผ่าน Web เบิ้มๆไปเลย อยาก Reload Log ก็กด Refresh รัวๆเท่าที่ใจเธอต้องการ (เผื่อรอ Auto Reload แล้วไม่ทันใจ)
GitHub Action
ก่อนหน้านี้เราได้ทดลองติดตั้ง Docker Image ลงบน Azure App Service ด้วยมือกันแล้วนะครับ ซึ่งในการทำงานจริงๆเราต้องพยายามปรับปรุงการใช้เวลาของเราให้เหมาะสม เพราะงั้นการทำให้ขั้นตอนการติดตั้งทำได้อัตโนมัติจึงเป็นเรื่องที่จำเป็นสำหรับเรามากๆเลยครับผมเพื่อให้มั่นใจว่าเราสามารถส่งมอบงานถึงมือลูกค้าได้ไวเพื่อให้ได้ทดลองใช้งานและนำ Feedback มาปรับปรุงระบบให้ดียิ่งขึ้นนะครับ
ในขั้นตอนสุดท้ายนี้จะเป็นการ Setup ให้ระบบทำการ Auto Deploy ให้เราเลยครับผม โดยจะมีขั้นตอนตามภาพนี้นะครับ
ขั้นตอนแรกให้เรากลับไปที่ Azure App Service ของเรานะครับ เลือก Deployment Center -> Setting -> เลือก GitHub Action เพื่อเปลี่ยนการ Deployment ของเราให้เป็นแบบอัตโนมัติด้วย GitHub Action แทนครับผม
ในส่วนของ GitHub Actions ให้เราระบุ Repository ที่เราเก็บ Code ที่ต้องการสั่ง Deployment นะครับ รวมถึงเลือก Branch ของเราด้วยว่าถ้า Commit ไปที่ Branch ไหนแล้วจะเริ่มต้นสั่ง Deployment ครับ ในที่นี้ก็ Main เยย
ในส่วนของ Registry settings ก็คงค่า Default ที่เราเลือกไปครับ จะเป็นการระบุว่าเวลาที่มีการ Deployment เกิดขึ้นนั้น จะมีการสร้าง Docker Image และ Push ไปที่ ACR ที่เราระบุครับผม ส่วน Tag นั้น GitHub ทำให้อัตโนมัติคร้าบบบ
เสร็จแล้วให้เราลองกด Preview ดู จะพบว่า Azure จะสร้าง GitHub Action Pipeline ให้ดังนี้นะครับ (ขั้นตอนคร่าวๆก็ตามภาพข้างบนเลย)
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions
name: Build and deploy container app to Azure Web App - ttssreactazureamd
on:
push:
branches:
- main
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to container registry
uses: docker/login-action@v2
with:
registry: https://ttssreactazure.azurecr.io/
username: ${{ secrets.AzureAppService_ContainerUsername_69666a375480421e9cb40acacdd9e947 }}
password: ${{ secrets.AzureAppService_ContainerPassword_44596b20cc7643358e82d9d3fbf29a37 }}
- name: Build and push container image to registry
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: ttssreactazure.azurecr.io/${{ secrets.AzureAppService_ContainerUsername_69666a375480421e9cb40acacdd9e947 }}/react-azure-amd:${{ github.sha }}
file: ./Dockerfile
deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
steps:
- name: Deploy to Azure Web App
id: deploy-to-webapp
uses: azure/webapps-deploy@v2
with:
app-name: 'ttssreactazureamd'
slot-name: 'production'
publish-profile: ${{ secrets.AzureAppService_PublishProfile_b57e6e4b9ffe4583912aba8d443edb8d }}
images: 'ttssreactazure.azurecr.io/${{ secrets.AzureAppService_ContainerUsername_69666a375480421e9cb40acacdd9e947 }}/react-azure-amd:${{ github.sha }}'
เมื่อเรากด Save ระบบก็จะเริ่ม Build Pipeline ให้ในครั้งแรกเลยนะครับ เราสามารถกดที่ Build Logs เพื่อไปยัง GitHub Action ต่อ
ทีนี้ อยากให้เราลองไปแอบส่องนิดนึงว่าทำไม GitHub Action มันถึงเชื่อม Azure App Service และ ACR ของเราได้นะครับ
สาเหตุหลักๆก็เป็นเพราะว่า Azure ช่วยสร้าง Secret ไว้ใน GitHub Action และทำการใช้งานใน GitHub Pipeline เลยครับผม ชีวิตสะดวกจัดๆแล้วจังหวะนี้
มี GitHub Pipeline ให้ดูที่นี้นะครับ ทำให้เราสามารถมาแก้ต่อทีหลังได้สบายเลย
ถ้าย้อนมาดูที่ App Service เราจะพบ Log อย่างสวยงามครับ
เราจะเห็นว่ามี Imageใหม่ถูก Deploy เข้า ACR และถูกนำมารันที่ App Service แล้วนะครับ
การบ้านครับ
เผื่อท่านใดอยากลอง Image อื่นๆนอกเหนือจาก Node.js/React สามารถทดลองตาม Repository ข้างล่างนี้ได้เลยนะครับ
สำหรับ Docker Image ที่ไม่ได้ใช้ Port 80 ผมแนะนำให้เพิ่มข้อมูลใน Azure Configuration ดังนี้นะครับ
WEBSITES_PORT
ในการ Bind Docker Port เช่น WEBSITES_PORT = 3000 จะหมายถึง-p 80:3000
WEBSITES_CONTAINER_START_TIME_LIMIT
เป็นเวลาที่ใช้รัน Docker ใน Azure ควร Set ไว้มากสุดที่1800
วินาที
.NET and Node.js
Python
Spring Boot
สรุป
ขอบคุณผู้อ่านทุกท่านที่มาถึงหัวข้อสรุปสุดท้ายนะครับ ผมคาดหวังว่าอย่างน้อยๆผู้อ่านจะมองเห็นภาพและแนวทางการทำงานในการติดตั้ง Docker Image ลงบน Azure App Service และสามารถทำงานได้อย่างต่อเนื่องและรวดเร็วในการติดตั้งครั้งถัดๆไป ด้วยความเชื่อมโยงของ App Service, ACR และ GitHub Action ในการสร้าง Pipeline เพื่อ Continous Deployment นะครับ
ขอให้มีความสุขกับการเขียน Code นะครับ
นายป้องกัน : )