Tensorflow 설치 오류

Python 의 pip 를 이용해서 Tensorflow 를 설치할때에 다음과 같은 오류가 날 수 있다. 2025년 4월 현재 오류가 발생 했다.

뭐라뭐라 알수 없는 오류가 나오는데, 해결책이 있다.

numpy 1.x 버전 호환 문제

Tensorflow 는 현재 numpy 2.x 와 잘 호환되지 않는 것으로 보인다. numpy 는 Pandas 를 설치하면서 함게 설치가 되는 것인데, 이때에 2.x 버전이 설치된다. 따라서 numpy 2.x 를 언인스톨하고 numpy 1.x 로 설치해준다.

이렇게 하고 나서 Tensorflow 도 언인스톨을 하고 재설치 해준다.

TF_ENABLE_ONEDNN_OPTS=0 문제.

이 문제는 정확하게는 다음과 같다.

I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable TF_ENABLE_ONEDNN_OPTS=0.

다른 연산 순서로 인해 부동 소수점 반올림 오류가 발생하여 약간 다른 수치 결과가 나타날 수 있습니다.

부동 소수점 반올림 오류 발생 가능성이 있다는 것인데, 이것을 없애기 위해서 환경변수를 설정해야 하라는 것이다. Python 코드로 해결하는 방법은 다음과 같다.

Linux 시스템이라면, Bash 쉘을 사용중이라면, 다음과 같이 쉘 환경변수를 지정하면 된다.

Windows 11 시스템이라면 ‘환경변수(Environment Variables)’ -> ‘시스템 변수(Under System Variables)’ 에 TF_ENABLE_ONEDNN_OPTS 이름으로 새로운 변수를 생성하고 값을 0 으로 지정하면 된다.

GenAI and LLM

AI 를 배울려면 어려운것이 용어다. 알수 없는 용어들. 기존 IT는 그래도 여기저기 주워들은(?) 게 있어서 한번 들으면 그런대로 잘 이해가 되는데, AI 분야는 한번 들었다고 해서 그게 바로 머리속에 자리잡는건 아닌듯 했다. 그래서 용어를 정리해야할 필요가 있다.

GenAI

Generate AI 를 줄여서 쓰는 말이다. 흔히 AI 라고하면 GenAI 를 말하는 것으로 착각하지만 불과 몇년전만 하더라도 AI 는 Specialized AI 였다.

GenAI는 텍스트(Text), 이미지(Images), 오디오(Audio), 비디오(Video)와 같은 새로운 콘텐츠를 생성할 수 있는 시스템을 의미한다. Specialized AI 는 특정 태스크에 특화된 AI를 말하는데, 이미지를 조정하거나 음성인식등인데 사람이 생성한 컨텐츠와 구별되는 특정한 태스크를 위해 디자인 된 것을 말한다. 반면에 GenAI 는 사람이 생성한건지, AI 가 생성한건지 구별하기가 매우 어렵다.

GenAI 는 어마어마한 데이터를 학습한 neural networks (NHs) 와 같은 machine learning(ML) 를 사용한다. 학습하는 방법은 훈련 데이터(Training Data) 를 가지고 패턴(Pattern)과 구조(Structures) 를 학습하고 이를 기반으로 데이터의 확률 분포를 모델링하고 이를 기초로해서 새로운 패턴과 구조를 다시 만들어낸다.

여기서 중요한것이 Training Data 를 가지고 패턴(Pattern)과 구조(Structures) 를 파악하는게 아니라 학습한다는데 있다. 이 학습을 위해서 방대한 데이터가 필요하고 이 방대한 데이터를 정제해서 훈련 데이터(Training Data) 를 다시 만들어야 한다.

따라서 데이터를 정제시키는 방법이 필요한데, Python Pandas 가 시작점이 될 수 있다. Pandas 를 데이터를 DataFrame 으로 전환시켜서 각종 연산을 통해서 훈련 데이터를 생성해준다.

LLM

Large Language Model. 한국어로는 대형 언어 모델이라고 한다. 여기서 용어에 의미를 명확히 해야 한다. 대형(Large) 와 모델(Model) 이라는 말이 함께 쓰이기 때문에 모델 자체에 어떤 대형이 있을 거라 생각하겠지만 여기서 대형(Large)는 훈련 데이터(Training Data) 가 아주 크다는 것을 말한다.

AI 는 모델을 만들기 위해서는 훈련 데이터, 그전에 정제되지 않은 Raw Data 가 필요한다. LLM 은 이 쌩짜(Raw Data) 와 훈련 데이터가 어마어마하게 크다는 것을 의미 한다.

GenAI 에서 가장 발전이 많하는 분야이고 자주 접하는 분야가 LLM 인데, 최종적으로 LLM 을 통해서 자연어 생성(NLG, Natural language generation)이 목적이다. LLM 은 GenAI 에서 인간의 언어를 이해고 인간의 언어를 생성하도록 특화된 신경망(neural networks) ML 이라고 볼 수 있다. GenAI 는 그야말로 다양한 능력을 가지는 AI 인데, LLM 은 그중에서 인간의 언어에 초점이 맞춰진 것이라고 할 수 있다.

인간의 언어를 이해한다….. 어찌보면 인간이 어렸을적에 언어를 배우는 방법과 비슷한데, 수많은 단어를 학습하고 언어 구조를 파악하고 종합해 뜻을 이해하고 단어와 구조를 이용해 다시 말을 하게 된다.

따라서 LLM 도 어마어마한 인간의 단어를 학습해야 한다. 그리고 그 언어의 구조도 학습해야 한다. 인간의 언어를 학습한 모델은 그 자체도 어마어마하게 클 것으로 예상되는데, 그런면에서 대형(Large) 라는 말은 훈련 데이터의 크기뿐만 아니라 그것을 학습한 AI 모델도 매우 큰 규모라는 것을 뜻하기도 한다. 또, 당연히 LLM 은 학습 데이터가 많아지면 많아질 수록 더 좋은 성능을 나타낼 것은 당연하다.

여기서 LLM 의 성능이라고 하면, 좀 더 인간에 가까운 텍스트 컨텐츠라고 할 것이다. 이를 위해서 더 많은 단어와 구조를 학습해야 한다. 더 많이 학습하면 할수록 인간과 유사한 자연스러운 텍스트 컨텐츠를 생성하게 된다. 결국에는 인간과 구별되지 않는 창조적인 대규모 자연어 콘텐츠를 자동으로 생성하는게 최종 목표라 할만 하다.

Deep Learning

딥러닝(Deep Learning)은 머신러닝(Machine Learning)의 한 분야로, 인간의 뇌 구조를 모방한 인공 신경망(Neural Networks)을 이용하여 복잡한 패턴을 학습하는 방식. 머신러닝은 다양한 알고리즘을 통해 데이터를 분석하고 예측 모델을 만드는 것을 의미하며, 딥러닝은 이러한 머신러닝 알고리즘 중에서도 특히 인공 신경망을 활용한 심층 학습을 강조한다.

딥러닝은 한걸음 더 나가서 스스로 학습한다는 것이다. 인공 신경망이기 때문에 가능 이야기다.

Transformer

어렵다… 이건 어려운 주제다. 쉽게 이해가 가지 않는 것이, 논문수준의 이론적 이해를 필요하기 때문이다. 기존의 이론에서 Attention Machanism 만 가지고 언어 추론을 한다고 하는데…

뭔 소리여?

일단 넘어가고 나중에 이해해 보자. 지금은 몰라도 된다.

Iterm2 설정

처음 Mac 을 구매하게 되면 제일 먼저 하는 것이 Iterm2 를 설치하는 것이다. Mac 에서 제공하는 Term 보다 사용하기 편해서 기본적으로 다 설치를 하게 된다. Iterm2 는 테마, 폰트등을 설정할 수 있고 Git 을 이용할 경우에 터미널에서 Branch 상태등을 잘 보여줘서 거의 필수로 설치를 하게된다.

기본상태

Iterm2 를 설치하면 다음과 같다.

프롬프트나 이런것들이 기본상태이다.

oh-my-zsh 설치

oh-my-zsh 는 zsh 에 많은 기능을 부여해준다. 대표적인 것이 Themes, Fonts 등이다. 다음과 같이 설치할 수 있다.

설치를 위와같이 쉘 프롬프트가 변경 된다.

.zshrc

zsh 의 설정은 zshrc 파일에 담겨 있다. 여기에 보면 oh-my-zsh 를 활성화하도록 되어 있으며, 테마등을 설정할 수 있다.

powerlevel10k 테마

여러가지 테마가 있는데, powerlevel10k 테마가 제일 좋아 보인다. 사람마다 성향이 다르기 때문에 어느게 좋다할 수 없지만, 찾아보면 여러가지 테마가 있으니 이것저것 설치하고 변경해보고 하면서 자신에게 맞는 테마를 고르면 된다.

powerlevel10k 는 문답식으로 세팅을 진행하게 된다.

이제, .zshrc 에 ZSH_THEME 를 값을 “powerlevel10k/powerlevel10k” 로 지정한다.

문답설정

zshrc 에 테마를 설정하고 새로운탭으로 터미널을 열면 문답이 진행이 된다.

모든 설정을 하고 나면 위와같이 안내문과 함께 종료 된다.

만일 설정을 다시하고 싶다면 다음과 같이 하면 된다.

Azure Application Gateway 특징

Azure Application Gateway 에 특징이 있는데, 이에 대해 정리한다.

서브넷(Subnet)

Application Gateway 를 위한 전용의 서브넷이 있어야 한다. 서브넷에 다른 서비스가 있다면 선택이 되지 않는다. 예를들어 Subnet1 에 VM 을 생성했다고 하면 Application Gateway 서브넷으로 사용할 수 없다.

따라서 네트워크 설계를 할때에는 Application Gateway 를 위한 전용의 서브넷을 미리 생성을 해야 한다.

Private Subnet 도 가능

Application Geteway 를 생성할때에 Subnet 을 지정하게 된다.

위와같이 Private Subnet 을 지정할 수 있다. 이렇게 지정하고 난후에도 Application Gateway 에 동작하는데에는 아무런 문제가 되지 않는다.

Azure Subnet 에서는 Private Subnet 을 지정하는 옵션이 존재한다. 다음과 같이 설명되어 있다.

Private subnets enhance security by not providing default outbound access. To enable outbound connectivity for virtual machines to access the internet, it is necessary to explicitly grant outbound access. A NAT gateway is the recommended way to provide outbound connectivity for virtual machines in the subnet. 

OutBound Access 에 제한이 걸릴 뿐 Application Gateway 의 동작에는 아무런 문제가 되지 않는다.

Azure Load Balancer

Azure 를 하면서 여러가지 차이점을 느끼지만, 그 중 Azure Load Balancer 만큼 차이를 보여주는 것도 없어보인다. AWS 에서는 Network Load Balancer 에 대응되지만 기능이 몇가지 더 있음으로 해서 아키텍쳐에도 차이를 보인다.

SNAT

Azure LoadBalancer 는 SNAT 기능을 지원한다. Source NAT 라고 하는 것으로 내부 사설IP 를 외부 공인IP 로 변경해 주는 기능이다. 이 기능은 내부 사설망에서 외부 공인 인터넷망으로 통신을 가능하게 해주는 중요한 기능이다.

VM 기반 리소스를 생성할때에 보안상 대부분 사설 서브넷(Private Subnet) 에 위치시킨다. 그리고 서비스를 위해서 VM 앞단에 Load Balancer 를 위치킨다. 문제는 VM 자체적으로 인터넷 통신을 하려고할 때다. 공인IP 가 있다고 하더라도 사설 서브넷에 위치된 VM 은 라우팅으로 인해서 외부로 나갈 수 없어 인터넷 통신을 할 수 없다. AWS 의 경우 이 문제를 해결 하기 위해서 NAT Gateway 서비스를 이용해야 한다.

Azure 에서는 Load Balancer 를 이용하면 이러한 문제를 해결 할 수 있다. 물론 Azure 에서도 NAT Gateway 서비스가 존재하지만 Azure 아케텍쳐상 베스트 프렉티스(Best Practice) 가 아닌 것으로 보인다.

Azure VM 기준 아케텍쳐인데(Azure 공인문서), Frontend 와 Backend 에 위치한 VM들은 모두 사설 서브넷이며 인터넷 통신을 위해서 Load Balancer 를 이용하고 있다. NAT Gateway 를 이용하면 더 좋아 보이는데, 베스트 프렉티스로 되어 있지 않은 모양이다.

Load Balancer 생성

Azure 에 Load Balancer 는 L4 Layer 다. AWS 에 Network Load Balancer 에 대응되지만 SNAT 기능을 제공함으로써 인터넷 통신을 가능하게 해준다.

Load Balancer 생성화면에서 많은 절차가 필요하다는 것을 알수 있는데, Outbound rules 이 SNAT 기능을 사용하는 것이다. 물론 이 기능을 사용하기 위해서는 Inbound rules 설정에서 SNAT 기능을 사용하겠다고 활성화를 해줘야만 한다.

Port allocation

SNAT 설정할때에 문제가 되는 항목이 바로 Port allocation 이다. SNAT 자체가 VM 에서 출발하는 IP 를 변환해주는 것인데, 어쨋거나 Load Balancer 가 대신 공인 인터넷으로 접속을 하게 해주는 것임으로 결국에는 Load Balancer 가 출발지로 되고 결국에는 Client Port 가 있어야 한다.

문제는 VM 에서 인터넷 접속을 이것저것 하게 되면 단 하나의 Client Port 가지고는 되지 않는다. 브라우져를 통해 웹사이트 접속을 하더라도 동시에 몇십게의 Client Port 가 생성된다. 따라서 Load Balancer 입장에서는 뒷단에 있는 VM 들에게 무제한 접속을 허용, 다시 말해서 Client Port 를 할당해 줄수가 없게 된다.

이를 위해서 Azure 는 Port allocation 설정을 하도록 하고 있다. Load Balancer 의 Frontend IP 당 뒷단 VM 수를 조합해 VM 하나에 할당가능한 Port 의 개수를 정하도록 해 놨다.

Port allocation 의 방식을 Manually 로 하지 않고, Azure 에 위임할 경우에 Scale out 시에 뒷단 VM 의 연결이 안될 수도 있다. 위와같이 수동으로 하고 뒷단 인스턴스 개수를 지정해주는 것을 권장하고 있다.

이 설정을 하게되면 SNAT 설정이 완료된다. 이제 VM 에서 인터넷이 되는지 확인해보면 된다.

DNAT

Azure Load Balancer 는 DNAT 기능을 제공한다. 이 기능일 이용하면 Load Balancer 뒷단 VM 에 접속을 할 수 있다. 하지만 이 기능은 잘 사용하지 않는 것으로 보인다. 베스트 프렉티스 아키텍쳐에서는 Bastion 서버를 구성해서 사용하는 것으로 보이는데, 어쨌든 DNAT 기능을 제공한다.

SNAT 기능은 Outbound rule 이라고 한다면 DNAT 는 Inbound NAT rule 이라고 한다.

테스트를 위해서 SSH(22 port) 를 활성해서 뒷단 VM 에 접속하도록 했다. 이렇게 되면 Load Balancer 의 공인IP 를 기반으로 VM 에 22 포트로 접속이 가능해 진다. Frontend Port 의 경우에는 잘 알려진 포트가 아닌 다른 포트를 이용하는 것이 좋다.

결론

Azure Load Balancer 는 L4 Layer 장비다. AWS 와 달리 SNAT, DNAT 기능을 모두 지원함으로써 뒷단 VM 에 특정한 기능을 제공한다.

Azure 기술 문서에는 사설 서브넷에 위치한 VM 의 인터넷 연결을 위해서 Load Balancer 를 이용하고 있다. VM 접속을 위해서는 Bastion 서비스를 이용하도록 구성하고 있어서 Inbound NAT Rule 은 임시적으로 긴급한 상황이 아니라면 크게 사용할 일은 없어보인다.

Ingress-Nginx 설치

Kubernetes 에서 인기있는 Ingress Controller 로 Nginx 가 있다. 설치는 Helm 을 이용하면 된다.

설치 명령어를 보면, 여러가지 파라메터가 보이는데 kube-prometheus-stack 으로 설치한 prometheus 까지 고려한 것이다.

관련 명령어나 파라메터는 다음의 주소에 매우 잘 설명되어 있어서 별도로 추가설명은 하지 않는다.

쿠버네티스 prometheus 설정

Helm 을 이용해 kube-prometheus-stack 을 설치하게 되면, Grafana 를 통해서 통계 그래프를 볼 수 있다. 그런데, 몇몇 대쉬보드는 나타나지 않는데 여기서는 이 문제를 해결하는 방법을 알아본다.

Prometheus

프로메테스에서 Status > Target health 에서 상태를 같이 보면서 해결하면 좋다. 어떻게 스크랩이 설정되어 있는데, 상태가 DOWN 인 부분을 잘 살펴보면 된다.

CoreDNS

CoreDNS 대쉬보드가 아무것도 나오지 않는다. Service 에 kube-dns 혹은 Helm 으로 설치할 경우에 coredns 로 나오는데, describe 를 한번 해본다.

위에 내용을 보면 k8s-app=coredns 가 보인다. 이제 Service 에 prometheus-coredns 를 한번 보자.

Selector 를 보면 k8s-app=kube-dns 로 보인다. 이것이 맞지 않아서 관련 내용을 가지고 오지 못하는 원인다. 이것을 k8s-app=coredns 로 바꿔 준다.

이렇게 하고 Prometheus 에 Status > Target health 에서 coredns 를 보면 스크랩이 잘되는 것으로 바뀐다.

Etcd

Prometheus 에서 Status > Target health 에서 ‘http://192.168.96.60:2381/metrics‘ 로 EndPoint 로 설정되어 있다. curl 로 긁어보면 다음과 같이 나온다.

Etcd 의 경우에는 상태 모니터링을 위한 파라메터 설정을 해줘야 한다. 하지만 kubeadm 으로 K8S 를 설치한 경우에 etcd 는 Master Controll Plane 으로 동작하기 때문에 파라메터 수정을 위해서는 manifest 파일을 수정으로 해야 한다.

2381 포트를 보면, 127.0.0.1 만 엔드포인트로 되어 있다. 192.168.96.60 을 추가해 준다. 그러면 정상화 된다.

Controller Manager

Prometheus 에서 Status > Target health 에서 ‘dial tcp 192.168.96.60:10257: connect: connection refused’ 에러 메시지가 나타난다.

Controller Manager 또한 Master Controller Plane 이기 때문에 manifest 파일을 수정해줘야 한다.

bind-address 주소가 localhost 로 되어 있기 때문에 refused 된 것으로 보인다. 0.0.0.0 으로 바꾼다.

kube-scheduler

kube-scheduler 도 위에 controller manager 처럼 bind-address 가 localhost 로 되어 있다. 0.0.0.0 으로 바꾼다.

kube-proxy

kube-proxy 는 DaemonSet 오브젝트다. 설정 파일이 있는 것처럼 나오지만 설정파일은 없고, 대신 ConfigMap 으로 존재한다. 이 ConfigMap 에 metricsBindAddress 값이 없다. 이 값을 0.0.0.0 으로 지정해 준다.

DaemonSet 은 자동으로 재시작되지 않는다. Rollout 업데이트를 해준다.

kube-prometheus-stack 설치

기존에 prometheus 설치를 했었는데, 버전을 올릴겸해서 다시 설치를 했다. 설치는 Helm 을 이용했고, 기존에 설치된 버전을 삭제 처리 했다.

환경

K8S 환경이 매우 중요하다. 필자의 환경은 Handy way 방법으로 설치를 진행한 상황이기 때문에 etcd, apiserver, scheduler 가 OS 데몬으로 운영되고 있다. 따라서 Master, Worker 노드의 IP 를 필요로 한다.

만일 kubeadm 으로 설치했다면 selector 를 이용해서 라벨을 선택해주면 된다.

삭제

Helm 을 이용해서 설치했기 때문에 Helm 으로 삭제를 해야 한다. 여기서 한가지 주의해야 하는 것이 있는데, Helm 으로 삭제를 하여도 CRD(Custom Resource Definition) 은 삭제되지 않기 때문에 수동으로 삭제를 해줘야 한다.

남아 있는 crd 가 다를 수가 있지만, 반드시 삭제를 해줘야 문제가 발생하지 않는다. crd 는 다른 프로그램에서도 삭제가 되지 않는 경우가 많다.

Helm repo update

Helm 저장소를 업데이트를 해야 한다.

Download a chart and untar

Helm Chart 를 다운로드하고 압축해제 한다. 이렇게 함으로써 values.yaml 파일을 수정할 수 있게된다.

다운로드와 함께 압축을 해제해 준다.

Values.yaml 파일 수정

어떤 내용들을 수정할지는 어떤 것을 모니터링 할지, 어떤 부분이 있는지에 따라서 달라진다. 변경된 내용은 대략 다음과 같다.

nodeSelector 에 system.rule: monitoring 이 보이는데, 이는 Worker 노드에 라벨을 말한다. 선택된 라벨을 가지고 있는 Worker 노드에 Prometheus 가 설치된다. 단, 이것도 상황에 따라서 달라지는데 Node exporter 의 경우에는 각 노드에 설치가 되어야 하기 때문에 라벨을 설정하지 않는다.

Node 라벨 생성

system.rule: monitoring 이라는 라벨을 생성해야 한다.

이렇게 하면 kworker3 에 prometheus 가 설치된다.

Dependency Update

Helm 을 이용해서 설치해야 한다. 그전에 kube-prometheus-stack 에 의존성이 있는 패키지를 업데이트 해줘야 한다.

설치

Helm 을 이용해 수정한 values.yaml 을 가지고 설치를 한다.

설치가 완료 되면 Pod, Service 에 Prometheus 가 올라오게 된다.

K8S dnstools 사용하기

K8S 를 사용하다보면 여러가지 검증을 위해 Pod 를 필요로 하는 경우가 있는데, dnstools 이 유용하다. 다음과 같이 사용할 수 있다.

Pod 가 생성되면서 dnstools 컨테이너에 진입하게 되고 exit 를 하게 되면 Pod 는 삭제된다.

혹은 curl 이미지를 이용하는 방법도 있다.