by Velucid ⏲ May 18, 2021
OCI OKE(Oracle Kubernetes Engine) 따라하기
OCI OKE 운영을 위한 기본 환경 구성
OCI OKE에 애플리케이션 배포하기
OCI OKE(Oracle Kubernetes Engine)에서 Guestbook(방명록) 애플리케이션을 배포하는 과정을 보여드립니다.
OCI Load Balancer를 통해 외부 사용자의 접근이 가능하며, 이는 3 개의 Replica로 구성된 PHP 기반 Guestbook으로 트래픽을 분배합니다. 방문 이력 데이터는 MongoDB에 저장/관리 됩니다.
먼저, MongoDB를 Cluster에 배포하는 과정을 살펴 봅니다. NAT Gateway를 통해 Docker Hub를 통해서 직접 MongoDB 이미지를 가져옵니다.
참고: 외부 접근이 불가능한 환경에서는, 도커 이미지를 OCI Repository에 등록하여, 보다 안전하게 관리하고 사용할 수 있습니다.
MongoDB 이미지는 Docker Hub에 공식 등록된 mongo:4.2를 사용합니다.
$ cat mongo-deployment.yaml
xxxxxxxxxx
apiVersion apps/v1
kind Deployment
metadata
name mongo
labels
app mongo
tier backend
spec
selector
matchLabels
app mongo
tier backend
replicas1
template
metadata
labels
app mongo
tier backend
spec
containers
name mongo
image mongo4.2
args
--bind_ip
0.0.0.0
resources
requests
cpu 100m
memory 100Mi
ports
containerPort27017
xxxxxxxxxx
$ kubectl apply -f mongo-deployment.yaml
deployment.apps/mongo created
xxxxxxxxxx
$ kubectl get pods -l app=mongo
NAME READY STATUS RESTARTS AGE
mongo-7487b96cf9-v7wcn 0/1 ContainerCreating 0 16s
...
$ kubectl get pods -l app=mongo
NAME READY STATUS RESTARTS AGE
mongo-7487b96cf9-v7wcn 1/1 Running 0 67s
xxxxxxxxxx
$ kubectl describe pod mongo
Name: mongo-7487b96cf9-v7wcn
Namespace: default
Priority: 0
Node: 10.0.10.155/10.0.10.155
Start Time: Tue, 04 May 2021 07:33:30 +0000
Labels: app=mongo
pod-template-hash=7487b96cf9
tier=backend
Annotations: <none>
Status: Running
IP: 10.244.0.149
IPs:
IP: 10.244.0.149
Controlled By: ReplicaSet/mongo-7487b96cf9
Containers:
mongo:
Container ID: docker://1ecbc8c0653...............9d3e1685764
Image: mongo:4.2
Image ID: docker-pullable://mongo@sha256:81d1b57c9........7601ec1bd9e0fc9
Port: 27017/TCP
Host Port: 0/TCP
Args:
--bind_ip
0.0.0.0
State: Running
Started: Tue, 04 May 2021 07:33:31 +0000
Ready: True
Restart Count: 0
Requests:
cpu: 100m
memory: 100Mi
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-x9xxl (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-x9xxl:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-x9xxl
Optional: false
QoS Class: Burstable
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events: <none>
xxxxxxxxxx
$ kubectl describe pod mongo | grep Node:
Node: 10.0.10.155/10.0.10.155
xxxxxxxxxx
$ kubectl logs -f deployment/mongo
...
2021-05-04T07:38:47.454+0000 I NETWORK [listener] connection accepted from 127.0.0.1:51824 #1 (1 connection now open)
2021-05-04T07:38:47.455+0000 I NETWORK [conn1] received client metadata from 127.0.0.1:51824 conn1: { application: { name: "MongoDB Shell" }, driver: { name: "MongoDB Internal Client", version: "4.2.13" }, os: { type: "Linux", name: "Ubuntu", architecture: "x86_64", version: "18.04" } }
2021-05-04T07:41:34.853+0000 I NETWORK [conn1] end connection 127.0.0.1:51824 (0 connections now open)
xxxxxxxxxx
$ kubectl exec -it mongo-7487b96cf9-v7wcn -- bash
...
root@mongo-7487b96cf9-v7wcn:/# env
...
HOSTNAME=mongo-7487b96cf9-v7wcn
GPG_KEYS=E162F504A20CDF15827F718D4B7C549A058F8B6B
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_PORT=tcp://10.96.0.1:443
PWD=/
HOME=/root
GOSU_VERSION=1.12
MONGO_VERSION=4.2.13
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP_PORT=443
MONGO_REPO=repo.mongodb.org
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
TERM=xterm
MONGO_PACKAGE=mongodb-org
SHLVL=1
MONGO_MAJOR=4.2
KUBERNETES_SERVICE_PORT=443
JSYAML_VERSION=3.13.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_SERVICE_HOST=10.96.0.1
_=/usr/bin/env
...
root@mongo-7487b96cf9-v7wcn:/# cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.244.0.143 mongo-7487b96cf9-v7wcn
...
MongoDB에는 등록된 사용자 데이터가 없음을 확인 합니다. 추후에 Guestbook을 통해 자동 생성된 방명록 테이블을 확인 가능합니다.
xxxxxxxxxx
root@mongo-7487b96cf9-v7wcn:/# mongo
MongoDB shell version v4.2.13
...
MongoDB server version: 4.2.13
Welcome to the MongoDB shell.
...
> show databases
admin 0.000GB
config 0.000GB
local 0.000GB
>
> db;
test
>
> show tables
>
> use admin;
switched to db admin
>
> show tables
system.version
> exit
bye
xxxxxxxxxx
root@mongo-7487b96cf9-v7wcn:/# exit
MongoDB 접속을 위한 K8S Service를 생성합니다. Service의 용도는 항상 고정된 IP로 MongoDB에 대한 접근을 가능하게 하기 위해서 생성합니다. 노드의 재시작 등 여러 이유로 MongoDB Pod가 제시작 하면, 해당 Pod의 IP는 항상 바뀌게 됩니다. ClusterIP Service를 생성해 놓으면, 이를 통해 항상 고정된 네트워크 경로 설정이 가능해집니다.
xxxxxxxxxx
$ cat mongo-service.yaml
xxxxxxxxxx
apiVersion v1
kind Service
metadata
name mongo
labels
app mongo
tier backend
spec
selector
app mongo
tier backend
type ClusterIP
ports
port27017
targetPort27017
xxxxxxxxxx
$ kubectl create -f mongo-service.yaml
service/mongo created
xxxxxxxxxx
$ kubectl get service -l app=mongo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mongo ClusterIP 10.96.214.141 <none> 27017/TCP 34s
xxxxxxxxxx
$ kubectl describe service mongo
Name: mongo
Namespace: default
Labels: app=mongo
tier=backend
Annotations: <none>
Selector: app=mongo,tier=backend
Type: ClusterIP
IP Families: <none>
IP: 10.96.214.141
IPs: <none>
Port: <unset> 27017/TCP
TargetPort: 27017/TCP
Endpoints: 10.244.0.144:27017
Session Affinity: None
Events: <none>
Docker Hub에 공식 등록된 paulczar/gb-frontend:v5 이미지를 사용합니다. Guestbook은 PHP 기반의 애플리케이션이며, "mongo"라는 이름으로 등록된 ClusterIP 서비스를 자동으로 연계하여 리파지토리로 사용합니다. 그러므로 MongoDB Pod 및 Service의 이름을 반드시 "mongo"로 배포해야 합니다.
xxxxxxxxxx
$ cat guestbook-deployment.yaml
xxxxxxxxxx
apiVersion apps/v1
kind Deployment
metadata
name guestbook
labels
app guestbook
tier frontend
spec
selector
matchLabels
app guestbook
tier frontend
replicas3
template
metadata
labels
app guestbook
tier frontend
spec
containers
name guestbook
image paulczar/gb-frontend v5
resources
requests
cpu 100m
memory 100Mi
env
name GET_HOSTS_FROM
value dns
ports
containerPort80
xxxxxxxxxx
$ kubectl create -f guestbook-deployment.yaml
deployment.apps/guestbook created
xxxxxxxxxx
$ kubectl get pod -l app=guestbook
NAME READY STATUS RESTARTS AGE
guestbook-95d666cf8-5lpf5 1/1 Running 0 12s
guestbook-95d666cf8-jbxcz 1/1 Running 0 12s
guestbook-95d666cf8-jztth 1/1 Running 0 13s
xxxxxxxxxx
$ kubectl describe pod guestbook-95d666cf8-jztth
Name: guestbook-95d666cf8-jztth
Namespace: default
Priority: 0
Node: 10.0.10.39/10.0.10.39
Start Time: Tue, 04 May 2021 14:27:30 +0000
Labels: app=guestbook
pod-template-hash=95d666cf8
tier=frontend
Annotations: <none>
Status: Running
IP: 10.244.1.19
IPs:
IP: 10.244.1.19
...
xxxxxxxxxx
$ kubectl describe pod guestbook-95d666cf8-5lpf5
Name: guestbook-95d666cf8-5lpf5
Namespace: default
Priority: 0
Node: 10.0.10.75/10.0.10.75
Start Time: Tue, 04 May 2021 14:27:30 +0000
Labels: app=guestbook
pod-template-hash=95d666cf8
tier=frontend
Annotations: <none>
Status: Running
IP: 10.244.0.18
IPs:
IP: 10.244.0.18
...
xxxxxxxxxx
$ kubectl describe pod guestbook-95d666cf8-jbxcz
Name: guestbook-95d666cf8-jbxcz
Namespace: default
Priority: 0
Node: 10.0.10.155/10.0.10.155
Start Time: Tue, 04 May 2021 14:27:30 +0000
Labels: app=guestbook
pod-template-hash=95d666cf8
tier=frontend
Annotations: <none>
Status: Running
IP: 10.244.0.147
IPs:
IP: 10.244.0.147
...
x$ kubectl exec -it guestbook-95d666cf8-jbxcz -- bash
...
root@guestbook-95d666cf8-jbxcz:/var/www/html# env | grep MONGO
MONGO_PORT_27017_TCP=tcp://10.96.214.141:27017
MONGO_SERVICE_HOST=10.96.214.141
MONGO_PORT_27017_TCP_PORT=27017
MONGO_PORT=tcp://10.96.214.141:27017
MONGO_PORT_27017_TCP_PROTO=tcp
MONGO_SERVICE_PORT=27017
MONGO_PORT_27017_TCP_ADDR=10.96.214.141
...
root@guestbook-95d666cf8-jbxcz:/var/www/html# cat guestbook.php
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
// echo extension_loaded("mongodb") ? "loaded\n" : "not loaded\n";
if (isset($_GET['cmd']) === true) {
$host = 'mongo';
if (getenv('GET_HOSTS_FROM') == 'env') {
$host = getenv('MONGO_WRITE_HOSTS');
}
$mongo_host = "mongodb+srv://$host/guestbook?retryWrites=true&w=majority";
header('Content-Type: application/json');
// Create Guestbook Post
if ($_GET['cmd'] == 'set') {
$manager = new MongoDB\Driver\Manager("mongodb://$host");
$bulk = new MongoDB\Driver\BulkWrite(['ordered' => true]);
$bulk->insert(['message' => $_GET['value']]);
try {
$result = $manager->executeBulkWrite('guestbook.messages', $bulk);
}
catch (\MongoDB\Driver\Exception\Exception $e) {
echo '{"error": "An error occured connecting to mongo ' . $host . '"}';
exit;
}
print('{"message": "Updated"}');
// Get Guestbook Post
} else {
$host = 'mongo';
if (getenv('GET_HOSTS_FROM') == 'env') {
$host = getenv('MONGO_READ_HOSTS');
}
$manager = new MongoDB\Driver\Manager("mongodb://$host");
$query = new MongoDB\Driver\Query([]);
try {
$cursor = $manager->executeQuery('guestbook.messages', $query);
}
catch (\MongoDB\Driver\Exception\Exception $e) {
echo '{"error": "An error occured connecting to mongo ' . $host . '"}';
exit;
}
$data = array();
foreach ($cursor as $document) {
$data[] = $document->message;
}
print('{"data": ' . json_encode($data) . '}');
}
} else {
phpinfo();
} ?>
OCI OKE Cluster를 배포하며, 내부적으로 OCI Load Balancer를 사용하도록 K8S Cluster가 자동을 설정되게 됩니다. 별도의 복잡한 연계 과정 및 Menifest 파일 설정 없이, Service type를 LoadBalancer로 설정하면 자동으로 OCI Load Balancer가 생성되고 K8S Cluster와 연동되어 사용할 수 있습니다.
xxxxxxxxxx
$ cat frontend-service.yaml
xxxxxxxxxx
apiVersion v1
kind Service
metadata
name frontend
labels
app guestbook
tier frontend
spec
type LoadBalancer
ports
port80
protocol TCP
targetPort 80
selector
app guestbook
tier frontend
xxxxxxxxxx
$ kubectl create -f frontend-service.yaml
service/frontend created
xxxxxxxxxx
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend LoadBalancer 10.96.82.77 <pending> 80:32570/TCP 44s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15d
mongo ClusterIP 10.96.214.141 <none> 27017/TCP 12h
...
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend LoadBalancer 10.96.82.77 132.2**.***.96 80:32570/TCP 108s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15d
mongo ClusterIP 10.96.214.141 <none> 27017/TCP 12h
xxxxxxxxxx
$ kubectl describe service frontend
Name: frontend
Namespace: default
Labels: app=guestbook
tier=frontend
Annotations: <none>
Selector: app=guestbook,tier=frontend
Type: LoadBalancer
IP Families: <none>
IP: 10.96.82.77
IPs: <none>
LoadBalancer Ingress: 132.2**.***.96
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 32570/TCP
Endpoints: 10.244.0.147:80,10.244.0.18:80,10.244.1.19:80
Session Affinity: None
External Traffic Policy: Cluster
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal EnsuringLoadBalancer 2m49s service-controller Ensuring load balancer
Normal EnsuredLoadBalancer 107s service-controller Ensured load balancer
Load Balancer 서비스의 EXTERNAL-IP로 등록된 IP를 통해 브라우저로 접근 합니다.
xxxxxxxxxx
$ kubectl exec -it mongo-7487b96cf9-v7wcn -- bash
root@mongo-7487b96cf9-v7wcn:/#
root@mongo-7487b96cf9-v7wcn:/# mongo
MongoDB shell version v4.2.13
...
> show databases
admin 0.000GB
config 0.000GB
guestbook 0.000GB
local 0.000GB
>
> use guestbook
switched to db guestbook
>
> show tables
messages
>
> db.messages.find({})
{ "_id" : ObjectId("60921996bc45b46b2f517892"), "message" : ",Queen" }
>
> exit
xxxxxxxxxx
$ kubectl delete -f frontend-service.yaml
service "frontend" deleted
$ kubectl delete -f guestbook-deployment.yaml
deployment.apps "guestbook" deleted
$ kubectl delete -f mongo-service.yaml
service "mongo" deleted
$ kubectl delete -f mongo-deployment.yaml
deployment.apps "mongo" deleted
참고 자료
- K8S Tutorial : MongoDB를 사용한 PHP 방명록 애플리케이션 배포하기