Information content (IC)은 언어학에서 나왔던 개념으로,문맥상에 어떤 개념이 나타내면, 이 개념이 나타내는 것은 정보의 양을 나타냅니다. 주로, 추상적인 개념(상위 개념)일수록 정보량이 많지 않기 때문에, IC값이 낮아지도록 표현이 되고, 구체적인 개념을 일컫을 수록 IC값이 높아지게 됩니다. IC값을 적절하게 설계했으면, 각 개념(또는 단어)가 얼마나 모호한지(=상위개념인지)를 알 수 있습니다. 그리고, 이러한 IC값을 온톨로지들 간의 유사도로 사용할 수 있습니다.
IC의 정의는 아래와 같습니다. 온톨로지 상에서 존재하는 모든 개념($C$)내에 특정 개념($c$) 은 $c \in C$이며, 0과 1사이 값을 가진다고 합니다[0, 1]. 그리고, 특정 두 개념이 IS-A 관계라고 하면, $c_{2}$가 상위텀이라면, $p(c_{1}) <= p(c_{2})$여야 합니다. 그리고, 최상위노드가 하나라면, 이 최상위 노드의 확률값은 1이 되게도록 확률을 정의합니다 [1]. 이때 IC값은 다음과 같습니다.
$IC = -log p(c)$
예를 들어, WordNet의 말뭉치내에, 아래와 같은 분류체계가 있다고합시다. 분류체계내에 하위텀으로 갈수록, 구체적이기 때문에, 빈도가 적게나오고 정보도 많을 것입니다, 반대로 상위텀일수록 추상적이 많이 나온 개념이기에, 위로갈수록 확률은 높아집니다. 이러다 극단적으로 최상위개념이면, 모든 개념을 포함하기에 확률을 1로 합니다. IC값은 logP(c)이므로, [0과 1]의 p값이 클수록 (=0.99999)에 가까울수록 0으로 수렴합니다. 반대로 0에 가까울수록 값이 엄청나게 음수가됩니다. 이 음수가된 것을 다시 -을 취하니 값은 커지게 됩니다 (Figure 2).
Resnik의 semantic similarity (1995)
1995년에 Resnik은 IC을 이용해서 두 개념이 의미론적으로 얼마나 유사한지를 측정할 수 있게 했습니다. 두 개념의 유사도를 측정하기위해서 아래의 공식을 사용합니다 [2].
where S(c_{1}, c_{2}) is the set of concepts that subsume both $c_{1}$ and $c_{c2}$
위의 개념에서 subsume은 "포함하다"를 의미합니다. 즉, c1, c2을 포함하는 집합을 의미하므로, 두 개념을 포함하는 상위개념들의 집합을 의미합니다. 두 개념을 포함하는 상위집합중에서 가장 IC가 높은것을 의미하므로, "두 개념을 포함하는 상위 개념중에 가장 아래에 있는 개념(=구체적인 개념)"을 의미합니다.
Figure 1에서 S(nickel, dime)은 COIN, CASH, MONEY, MEDIUM OF EXCHANGE...등을 포함하고, 두 개념의 유사도에 쓰이는 c은 이 중에거 가장 하위에 있는 것이기 때문에, COIN이 됩니다. 즉 COIN의 -logP(c)을 구하면됩니다. 이 P(c)은 문제마다 다르므로 구체적인 내용은 스킵합니다. 단, 이를 구하는 예시를 하나 들어보자면, 일반적인 자연어처리문제라면, 전체 corpus가 있을 것이고, 이 corpus에 단어가 전체 K개가 있다고 합시다. 그리고 워드넷이라는 taxonomy에 들어있는 개념이 h개 라면, 전체 N은 각 h가 corpus에 얼마나 있는지를 세어보면 알 것입니다. 그리고 N중에 실제 각 개념 c들이 얼마나 있는지 상대적인 빈도로 제시합니다.
[1] Ross, S. M. (2019).A first course in probability. Boston: Pearson.
[2] Resnik, P. (1995). Using information content to evaluate semantic similarity in a taxonomy.arXiv preprint cmp-lg/9511007.
master node에 쿠버네티스를 세팅하고, 각 노드랑 연결해보려고합니다. 쿠버네티스로 클러스터를 구성하는 것은 1) control-plane을 설치해야합니다(단일 control-plane또는 복수의 control-plane도 가능), 2) 또한, 각 워커노드들을 등로갛여 파드들의 네트웍이 클러스터내에서 잘 통신되게 하는 것입니다.
시작전에 앞서
kubeadm을 이용해서 쿠버네티스를 세팅해보려고합니다. kubeadm으로 쿠버네티스를 세팅하는 것이 쿠버네티스 공식홈페이지에서도 "best practice"라고 설명되어있습니다. kubeadm은 쿠버네티스를 간단히 설치해서 사용해보기에 용이합니다.
사용 전 확인사항
쿠버네티스를 사용하기전에 장치에 아래의 스펙요구사항을 만족하는지 확인해야합니다.
A compatible Linux host. The Kubernetes project provides generic instructions for Linux distributions based on Debian and Red Hat, and those distributions without a package manager.
2 GB or more of RAM per machine (any less will leave little room for your apps).
2 CPUs or more.
Full network connectivity between all machines in the cluster (public or private network is fine).
Unique hostname, MAC address, and product_uuid for every node. Seeherefor more details.
Certain ports are open on your machines. Seeherefor more details.
Swap disabled. YouMUSTdisable swap in order for the kubelet to work properly
마지막의 swap을 끄라고 되어있는데 이는 "kubelet"이라는 데몬이 정상적으로 돌게하기위함이라고 합니다. "kubelet"은 파드에서 컨테이너들이 "선언적으로 정의한"(=내가 세팅한 그대로 현 상태를 유지해주는 것)대로 실행되게끔 해주는 데몬입니다.
본 포스팅에서는 카카오클라우드를 이용해서 private network로 구성된 클러스터를 구성해볼 예정입니다. 사설 IP로 구성된 노드들은 아래와 같습니다.
MAC address가 유요한지 product uuid가 각 노드에서 유니크한지 확인합니다. 아래의 명령어를 이용해서, 각 노드에서 MAC adresss, product_uuid가 유일한지 확인해봅니다. 일단은 마스터노드만 확인해봅니다.
ubuntu@k8s-master:~$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback [.........]
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether [..........]
ubuntu@k8s-master:~$ sudo cat /sys/class/dmi/id/product_uuid
[...........]
K8S을 설치하기전에 필수로 설치해야할 것이 docer가 있습니다. Docker은 다음의 링크로 설치하시길바랍니다 [1]
kubeadm, kubelet, kubectl을 설치할 것입니다. 각 기능은 아래와같습니다.
kubeadm: 클러스터를 구축하고 실해나는 과정을 의미합니다 (=cluster bootstrap). control-plane, worker nodes을 구성하고 각 노드들끼리 올바른 정보들을 가지고있는지, 통신이 한지를 확인합니다. [2]
kubelet: 선언적으로 정의한 대로 파드를 시작하거나, 컨테이너를 시작해주는 기능을 합니다.
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
Then you can join any number of worker nodes by running the following on each as root:
# 하단의 명령어는 적을 수 있다면 잘 기록하시길 바랍니다.
kubeadm join 172.16.2.231:6443 --token y24p4r.7itfeqy96rkjof8q \
--discovery-token-ca-cert-hash sha256:5e9e43eadba4eb7858033de6fbb4d99cc811f7d49d96f3637435370463c087e
클러스터의 실행을 위해서 위의 나온 메시지대로 $HOME/.kube/config 파일을 설정합니다. (일반사용자로 전환후)
// 명렁어로 토큰 출력
# kubeadm token list
// 아래의 명령어로 해시 출력
# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
control-plane에서만 deployment을 실행하면 파드가 생성되지않음을 확인할 수 있습니다. deployment에는 replicas 수를 3으로 지정해놓았는데요. 할당할 worker노드가 없기 때문에 pending으로 나온 것으로 판단됩니다. 실제로 하나의 파드의 상태를 추적해보면 아래와 같습니다.
root@k8s-master:/home/ubuntu# kubectl apply -f controllers-nginx-deployment-yaml
deployment.apps/nginx-deployment created
# watch "kubectl get pods"
Every 2.0s: kubectl get pods k8s-master: Sun Jan 8 09:00:15 2023
NAME READY STATUS RESTARTS AGE
nginx-deployment-85996f8dbd-98xlx 0/1 Pending 0 118s
nginx-deployment-85996f8dbd-d9bhg 0/1 Pending 0 118s
nginx-deployment-85996f8dbd-kjk8t 0/1 Pending 0 118s
root@k8s-master:/home/ubuntu# kubectl describe pod nginx-deployment-85996f8dbd-98xlx
Name: nginx-deployment-85996f8dbd-98xlx
Namespace: default
Priority: 0
Service Account: default
Node: <none>
Labels: app=nginx
pod-template-hash=85996f8dbd
Annotations: <none>
Status: Pending
IP:
IPs: <none>
Controlled By: ReplicaSet/nginx-deployment-85996f8dbd
Containers:
nginx:
Image: nginx:1.14.2
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-h2w8s (ro)
Conditions:
Type Status
PodScheduled False
Volumes:
kube-api-access-h2w8s:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 49s default-scheduler 0/1 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..
root@k8s-test:/home/ubuntu# kubeadm join 172.16.2.231:6443 --token 8yxfot.ktr4irill5hqw6h7 --discovery-token-ca-cert-hash sha256:bd91d60981b5774add11106cd42e1f8d6db18b241b5fd529b0d638e713522494
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
control-plane (master node)로 돌아가, 파드가 정상적으로 실행되는지 확인합니다. nginx 서버 3개가 정상적으로 돌아가는 것을 확인할 수 있었습니다.
root@k8s-test:/home/ubuntu# curl localhost:30052
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
localhost말고, 외부에서 해당 노드로도 요청을 보내봅니다.
// 마스터노드에서 노드1의 사설IP:30052포트로 요청
ubuntu@k8s-master:~$ curl 172.16.2.253:30052
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
임상시험은 대부분 무작위 선정(Randomization)으로 시험군(case), 대조군(Control)을 나눈다. 이 시험군과 대조군에 중재를 할 때, 시험군 또는 대조군에 속해있는 대상자들이 모종의 이유로 중재를 못받게 되는 상황이 된다(중도 탈락, 중재 전 사망, 치료 불이행). 이 때, 어쩔수없는 이유로 중재를 못받은 사람을 분석에 포함해서 의학적 성과(outcome)을 측정해야하냐 말아야하나 고민되는 시점이 생긴다. IIT은 모종의 이유가 있다 하더라도, 일단 무작위 배정되었으면, 분석에 포함하는 것을 일컫는다(“once randomized, always analyzed”). 반대의 개념으로는 "Per-Protocol"이라고 하며, 중재를 못받은 샘플을 분석에 포함하지 않는 것을 의미한다.
0. PUSH할 원격저장소를 만든다. docker hub (https://hub.docker.com/) 에서 로그인 후, 원격저장소를 생성한다. github저장소처럼 "ID/원격저장소명"으로 주소가 만들어진다.
1. 도커 로그인
$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: [YourID]
Password: ******
Login Succeeded
2. Docker hub에 올릴 이미지를 찾는다. 여기서 필자는 REPOSITOY에 simple-nlp-app, TAG은 v1을 올릴 것이다. 그렇지만, 도커허브에 올릴 때는 ID/repository명 = docker image ls에서의 REPOSITORY 명이 동일해야한다. 따라서, 한번 변경이 필요하다.
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
simple-nlp-app v1 7108c8dbe574 6 minutes ago 2.73GB
simple-nlp-app v0 c82b9d4786fe 9 minutes ago 2.73GB
simple-nlp-app latest 798c7cf60156 2 days ago 2.72GB
nginx latest ac8efec875ce 6 days ago 142MB
alpine/git latest 22d84a66cda4 3 weeks ago 43.6MB
hubproxy.docker.internal:5000/docker/desktop-kubernetes kubernetes-v1.25.2-cni-v1.1.1-critools-v1.24.2-cri-dockerd-v0.2.5-1-debian 09d7e1dbc2c4 2 months ago 363MB
k8s.gcr.io/kube-apiserver v1.25.2 97801f839490 2 months ago 128MB
k8s.gcr.io/kube-controller-manager v1.25.2 dbfceb93c69b 2 months ago 117MB
k8s.gcr.io/kube-scheduler v1.25.2 ca0ea1ee3cfd 2 months ago 50.6MB
k8s.gcr.io/kube-proxy v1.25.2 1c7d8c51823b 2 months ago 61.7MB
k8s.gcr.io/pause 3.8 4873874c08ef 5 months ago 711kB
k8s.gcr.io/etcd 3.5.4-0 a8a176a5d5d6 6 months ago 300MB
k8s.gcr.io/coredns v1.9.3 5185b96f0bec 6 months ago 48.8MB
docker/desktop-vpnkit-controller v2.0 8c2c38aa676e 19 months ago 21MB
docker/desktop-storage-provisioner v2.0 99f89471f470 19 months ago 41.9MB
nginx 1.14.2 295c7be07902 3 years ago 109MB
dattarajrao/simple-app latest 3f4a466b587c 4 years ago 132M
3. 두 가지 방법이 있는데, 첫 번째는 docker image tag을 이용하는 방법.
docker image tag SOURCE_IMAGE[:TAG] TARGET[:TAG] . 이 명령어는 원본 이미지에서 타겟이이지로 리포지토리 명을 다시 테그를 달 수 있게한다.
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] : 이 명령어는 실행중인 컨테이너를 다시 이미지화하는 방법이다.
아래의 예시는 docker image tag을 이용한 방법이다. tag을 이용할 때, 미리 생성한 원격저장소의 이름과 동일하게 해준다. 그렇지 않으면 아래와 같이 에러가 뜬다.
$ docker push 4pymgalion/simple-nlp-app:v1
The push refers to repository [docker.io/4pymgalion/simple-nlp-app]
An image does not exist locally with the tag
따라서, 아래와 같이 원격저장소의 ID/저장소명과 같이 도커 이미지를 테그를 다시 달아준다. 그러면 추가로 "4pygmalion/simple-nlp-app"라는 리포지토리 이름을 가진 이미지가 생성된다.
$ docker tag simple-nlp-app:v1 4pygmalion/simple-nlp-app:latest
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
4pygmalion/simple-nlp-app latest 7108c8dbe574 14 minutes ago 2.73GB
simple-nlp-app v1 7108c8dbe574 14 minutes ago 2.73GB
nginx latest ac8efec875ce 6 days ago 142MB
alpine/git latest 22d84a66cda4 3 weeks ago 43.6MB
hubproxy.docker.internal:5000/docker/desktop-kubernetes kubernetes-v1.25.2-cni-v1.1.1-critools-v1.24.2-cri-dockerd-v0.2.5-1-debian 09d7e1dbc2c4 2 months ago 363MB
k8s.gcr.io/kube-apiserver v1.25.2 97801f839490 2 months ago 128MB
k8s.gcr.io/kube-scheduler v1.25.2 ca0ea1ee3cfd 2 months ago 50.6MB
쿠버네티스를 이용하면,파드를 통해 클라이언트들이 어플리케이션을 호출할 수 있다. 그러나, 클라이언트 입장에서는 각각 파드의 주소가 존재하는하고, 클라이언트는 어느 파드로 갈지 명시하지 않았는데도, 쿠버네티스가 알아서 각각의 파드가 처리하게 만들어준다. 즉, 네트워킹이 쿠버네티스에 의해서, 알아서 처리되는데, 이 네트워크 서비스를 제공할 수 있는 자원이 "서비스(Service)"이다. 즉, 파드로 실행중인 앱을 네트워크서비스로 노출하게 해주는 자원이다.
서비스를 이용하기위해 각 파드, 노드가 만족해야할 사항[1]
각 파드는 노드상의 모든 파드와 통신할 수 있어야함
노드상의 에이전트(데몬, kubelet)은 해당 노드의 모든 파드와 통신할 수 있어야함.
어플리케이션을 네트워크 서비스로 외부에 노출하기
아래와 같이, 디플로이먼트(deployment)와 서비스(Service)를 명시한 yaml파일을 각각 만든다.
그리고, 아래와 같이 클러스터 내부에서는 서비스가 가능하고, 각 클러스터의 IP을 확인할 수 있다.
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28h
my-nginx ClusterIP 10.108.171.235 <none> 80/TCP 26h
해당 클러스터의 내부
Trouble shooting: Cluster IP에 접속이 안될 때 -> Node Port로 외부 클라이언트가 접속할 수 있도록 변경 [2]
서비스하는 클러스터의 IP는 가상의 IP이기 때문에, 클러스터 내부에서 내부로만 서비스할 수 있다. 즉 위의 예시에서처럼 클러스터 IP가 10.108.171.235면, 이 클러스터로 들어가야만 "10.108.171.235:80"의 서비스를 요청하고 받을 수 있는 것이다. 이것을 해결하기, "Cluster IP"을 "Node Port"로 변경해주어야 한다.
Node Port은 외부 클라이언트가 노드로 접속하기 위한 포트를 의미한다. 이 노드 포트을 변경하려면 아래와 같이 kubectl expose을 이용한다.
expose을 하면 TYPE이 NodePort인 서비스가 하나 생성이된다. example-serivce라는 이름을 가진 서비스의 TYPE이 NodePort인 것을 잘보자. 이 클러스터의 가상 IP은 10.101.151.252이다. 이것은 여전히 가상IP이기 때문에 서비스를 요청할 수 없다. 다만, 이 노드의 원래주소(localhost, 또는 privateIP, publicIP)로 들어갈때, 30384포트로 요청을하면 알아서 80번으로 바꿔준다. 즉 포트포워딩으로 30384을 80으로 바꿔서 10.101.151.252로 요청해볼 수 있다.
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
example-service NodePort 10.101.151.252 <none> 80:30384/TCP 26h
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28h
$ curl localhost:30384
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>