Angular 12 with Minikube Docker Driver

Phai Panda
6 min readAug 13, 2021

--

เพื่อนๆที่รู้จัก K8s หรือ Kubernetes คงรู้จัก Minikube อยู่แล้ว หนนี้ผมอยากจะรัน Angular 12 ใน container ที่ local เครื่องผมเองครับ

เตรียมเครื่องมือ

  • Angular CLI (โหลดและติดตั้ง ที่นี่)
  • Minikube (โหลดและอ่าน ที่นี่)
  • Docker Desktop (โหลดและติดตั้ง ที่นี่)
  • เครื่อง macOS 2018 (เพื่อนคนไหนใช้ Windows ใน Doc เขาก็แนะนำไว้แล้ว)
ng --version

ผล
Angular CLI: 12.2.0
Node: 14.17.1
Package Manager: npm 6.14.13
OS: darwin x64

minikube version

ผล
minikube version: v1.17.1

docker -v

ผล
Docker version 20.10.7

เตรียมความรู้

เริ่มต้นด้วยการลบ driver เก่าออกเพื่อติดตั้ง Docker driver ตัวใหม่

minikube delete

ผล
🔥 Deleting “minikube” in hyperkit …
💀 Removed all traces of the “minikube” cluster.

minikube start --driver=docker

ผล
🔥 Creating docker container (CPUs=2, Memory=1987MB) …
🐳 Preparing Kubernetes v1.20.2 on Docker 20.10.2 …
▪ Generating certificates and keys …
▪ Booting up control plane …
▪ Configuring RBAC rules …
🔎 Verifying Kubernetes components…
🌟 Enabled addons: storage-provisioner, default-storageclass
🏄 Done! kubectl is now configured to use “minikube” cluster and “default” namespace by default

เอาล่ะก่อนจะไปต่อ ผมอยากอธิบายภาพนี้

Docker Desktop Daemon vs Docker Daemon inside Minikube

มือใหม่ควรทราบว่าเมื่อเราได้ติดตั้ง Docker Desktop และ Minikube ไว้ในเครื่องเรา (Local) แล้ว ทั้ง Docker Desktop และ Minikube จะมี Docker Daemon เป็นของตัวเอง (ไม่ยุ่งเกี่ยวกัน)

สังเกตตอนทำคำสั่งนี้ minikube start มันเขียนว่า preparing Kubernetes and Docker นั่นหมายความว่ามันมี Docker (Daemon) เป็นของตัวเองแยกจาก Docker Desktop

จากภาพข้างต้นถ้าต้องการให้ image ที่เกิดจากคำสั่ง docker build ใช้งานได้ที่ฝั่ง Minikube (โดยคำสั่ง kubctl run) เราจะต้อง push image นั้นไปไว้บน Docker Hub เพราะ Minikube จะ pull image ด้วย Docker Daemon ของมันเอง

*** รายระเอียดของ Kubernetes จากบทความที่ผมเคยได้เขียนไว้ ที่นี่

จากภาพขออธิบายความสำคัญของ Docker Daemon อีกนิด

  • มันคือ engine หรือ server ตัวหนึ่งที่เอาไว้ building, running และ distributing Docker containers
  • มันรับคำสั่งจาก Docker Client ซึ่งดั่งเดิมเป็นโปรแกรมแบบ command line ที่รับคำสั่งจากผู้ใช้งานอีกที เช่น docker build, docker pull, docker run
  • มันไม่สามารถรันใน OS อื่นใดนอกจาก Linux ดังนั้นทั้ง Windows และ macOS (และ OS อื่นๆที่ไม่ใช่ Linux) จะต้องสร้าง VM ขึ้นมาก่อนจึงจะสามารถใช้งาน Docker Daemon ได้ ฉะนั้นไม่ต้องแปลกใจที่คำสั่ง minikube start --driver=docker สามารถเลือก VM เองได้ก็เพื่อการนี้ ดูเพิ่ม ที่นี่
  • มันมีชีวิตอยู่ใน Docker Host (โปรแกรม Docker) และใน Docker Host นี้ยังประกอบไปด้วย Images และ Containers
Docker Daemon ใน Docker Host เครดิตภาพ ที่นี่

Let’s go

สร้างโปรเจกต์ Angular

ng new todo-list
todo-list project

ติดตั้ง Bootstrap 5

npm install bootstrap@5

angular.json

ระบุ CSS

todo-list add Bootstrap CSS
"./node_modules/bootstrap/dist/css/bootstrap.min.css"

หน้าตาชิ้นงานของเรา

app.module.ts

เพิ่ม ReactiveFormsModule

app.component.ts

taskControl = new FormControl('')
items: string[] = []
add() {
this.items.push(this.taskControl.value.trim())
this.taskControl.setValue('')
}

app.component.html

<div class="container-fluid">
<div class="row d-flex justify-content-center">
<div class="col-12 col-sm-6 bg-dark bg-gradient text-light">
<div class="mb-3">
<label class="form-label">Task</label>
<input class="form-control" [formControl]="taskControl">
</div>
<div class="mb-3 d-flex">
<button class="btn btn-primary ms-auto" (click)="add()">ADD</button>
</div>
</div>
</div>
<div *ngFor="let item of items; let i = index" class="row mt-3 d-flex justify-content-center">
<div class="col-12 col-sm-6 bg-dark bg-gradient text-light">
<div class="py-3">{{i + 1}}. {{item}}</div>
</div>
</div>
</div>

package.json

แก้ไขเลข version เป็น 1.0.0

build.sh

สร้างไฟล์ชื่อ bulid.sh ไว้ที่ root project ไฟล์นี้เอาไว้รันคำสั่ง docker build สำหรับ Winows อ่าน ที่นี่

#!/bin/bash
NAME=$(cat package.json | grep name | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[[:space:]]')
VERSION=$(cat package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[[:space:]]')
IMAGE=$NAME:$VERSION
docker build -t $IMAGE .

หรือจะเขียนแค่นี้ก็ได้

#!/bin/bash
docker build -t todo-list:1.0.0 .

Dockerfile

สร้างไฟล์ชื่อ Dockerfile ไว้ที่ root project ไฟล์นี้ถูกเรียก ณ​ path ที่ระบุไว้ใน bulid.sh โดย default สัญลักษณ์ dot หมายถึง root project

FROM node:14.17.5-alpine as build-stage
WORKDIR /app
COPY package.json /app
RUN npm install
COPY ./ /app
RUN npm run build -- --output-path=out
FROM nginx:1.21.1
COPY --from=build-stage /app/out /usr/share/nginx/html
COPY --from=build-stage /app/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]

nginx.conf

สร้างไฟล์ชื่อ nginx.conf ไว้ที่ root project ไฟล์นี้จะถูกเรียกโดยคำสั่งใน Dockerfile เพื่อกำหนดการ routing แก่ Nginx (ผมเลือกใช้ Nginx เป็น web server) ในกรณีที่เราติดต่อกับ backend ผ่าน RESTful APIs ดังนั้นสร้างเผื่อไว้ก่อน

server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri$args $uri$args/ /index.html;
}
}

จากนั้นที่ root project รันไฟล์ build.sh

sh build.sh

ผล

ติด TypeScript ต้องมีเวอร์ชันระหว่าง 4.0.0 และ 4.2.0
อื่ม…เป็นเพราะอะไรกันแน่นะ

ทดลองลบ node_modules แล้วรันใหม่

ผล

ตรวจดู Docker Image

docker images

หรือดูผ่าน Docker Dashboard

todo-list เวอร์ชันตามที่เรากำหนด

ทดสอบรันด้วย Docker

docker run -p 9000:80 todo-list:1.0.0

ผล

เอาล่ะ ในส่วนของ Docker ถือว่าเสร็จสิ้น ทำงานได้ปกติ มาดูในส่วนของ Kubernetes ผ่าน Minikube ซึ่งเป็นเป้าหมายของเรากันครับ

สร้าง k8s folder ไว้ ณ root project แล้วเริ่มเขียน Kubernetes scripts

k8s/front-deployment.yml

Kubernetes Deployment นั้นจะเรียกใช้งาน image อ่านเนื้อหาเพิ่มเติม ที่นี่

apiVersion: apps/v1
kind: Deployment
metadata:
name: todo-list
labels:
app: todo-list
spec:
replicas: 1
selector:
matchLabels:
app: todo-list
template:
metadata:
labels:
app: todo-list
spec:
containers:
- name: todo-list
image: todo-list:1.0.0
ports:
- containerPort: 80

รัน front-deployment.yml

kubectl apply -f k8s/front-deployment.yml

ผล

an image not found (image pull back off)

pod ไม่สามารถทำงานได้ เนื่องจาก pull image มาไม่ได้ เพราะไม่ได้ pull จาก Docker Hub หรือกล่าวได้ว่า image ที่ได้ถูกทำงานด้วย Docker Daemon ของ Docker Desktop หากจะให้ deployment นี้ทำงานได้จำต้อง push image ไปไว้ที่ Docker Hub เสียก่อน ทั้งหมดนี้คือตามความรู้เท่าที่เรามี

แต่เราจะไม่ทำแบบนั้น…

เพื่อแชร์ image ที่ได้ระหว่าง Docker Desktop กับ Minikube โดยไม่พึ่ง Docker Hub จะต้องหาความรู้เพิ่ม

และก็ได้ว่า eval $(minikube docker-env) คือคำตอบของเรา อ่านเพิ่มเติม ที่นี่

เขาบอกว่าเราสามารถใช้ Docker Daemon ที่อยู่ภายใน Minikube มา build image ได้โดยระบุคำสั่ง eval $(minikube docker-env) ก่อน จากนั้นจึงสั่ง docker build ตามปกติ

มาดวนกันเลยดีกว่า เปิด Terminal พิมพ์

eval $(minikube docker-env)

ลองคำสั่ง docker images จะปรากฏ image ทั้งหมดภายใน Minikube

all images inside Minikube

ดังนั้นรัน sh build.sh อีกครั้ง

use Minikube Docker Daemon build an image

ผล

todo-list image inside Minikube

เกิดเป็นภาพนี้

Docker Desktop & Minikube images area itself

ฮั่นแน่! รู้เลยใช่ไหมขั้นตอนถัดไปผมจะทำอะไร

Kubernetes นั้นพยายาม pull image เป็นระยะอยู่แล้ว ในเมื่อ image พร้อมแล้วใน Minikube มันจึง pull สำเร็จ ลองไปตรวจดูสิครับ

kubectl get pod

ผล

an image is running

คราวนี้มาสร้าง service เพื่อเรียกใช้งานกัน ใน k8s folder สร้างไฟล์ชื่อ front-service.yml

k8s/front-service.yml

Kubernetes Service มีหลายประเภท ที่เราต้องการคือ NodePort อ่านเพิ่มเติม ที่นี่

apiVersion: v1
kind: Service
metadata:
name: todo-list
spec:
type: NodePort
selector:
app: todo-list
ports:
- port: 80
nodePort: 30000

รัน front-service.yml

kubectl apply -f k8s/front-service.yml

ผล kubectl get services

todo-list service is running

ดูที่ Kubernetes Dashboard ว่าแต่ละออบเจ็กต์สถานะเป็นอย่างไร อ่านเพิ่มเติม ที่นี่

minikube dashboard

ผล

สีเขียวทั้งหมดแสดงว่าหมดห่วง

ทดสอบเรียกใช้งาน todo-list โดยจะต้องเรียกผ่านชื่อของ service

minikube service todo-list

ผล

call todo-list by service name

สรุปว่าภาระกิจของเราที่ต้องการเขียน Angular 12 ให้รันด้วย Kubernetes บน localhost (127.0.0.1) เป็นอันสำเร็จ!

เกล็ดความรู้อื่นๆที่น่าสนใจ

  • ทุกครั้งที่เปิด Terminal ใหม่จะต้องรันคำสั่ง eval $(minikube docker-env) ก่อนเสมอถ้าต้องการใช้งาน Docker Daemon ของ Minikube
  • หากต้องการยกเลิกใช้งาน Docker Daemon ของ Minikube ใช้คำสั่ง eval $(minikube docker-env --unset) อ่านเพิ่มเติม ที่นี่

อ้างอิง

--

--

No responses yet