[쿠버네티스] 각 언어의 빌드(워크플로우) C언어 & golang & python & java

신현식·2023년 3월 3일
0

구름_Kubernetes

목록 보기
13/25
post-thumbnail

각 언어의 빌드과정(워크플로우)

  • 각 프로그래밍 언어마다 빌드하는 과정이 다르고 특성도 다르다.

  • 이러한 각 언어의 빌드와 특성을 이해해야지만 개발자가 만든 코드가 git에 올라오면 코드를 빌드하고 Docker image로 만들고 쿠버네티스에 올리는(=배포) 작업을 할 수 있는 것이다.

  • git에 올라온 코드에 대해서 빌드하고 image로 만들고 K8S에 올리는 배포과정까지를 Automation하는 것을 CI/CD or pipelineing이라고 한다.

  • AWS에서 ubuntu이미지 20.04버전, t3.medium , 스토리지 40G, 인터넷에서 HTTP트래픽 허용 클릭한 보안그룹 생성, 키페어 생성(mykey.pem)을 적용한 인스턴스 생성

cd Dowmloads
# 사용자명, 퍼블릭 IP, 키페어
ssh ubuntu@54.180.144.102 -i .\mykey.pem

# 저장소 클론
git clone https://github.com/c1t1d0s7/example-hello.git

인터프리터 언어 VS 컴파일 언어

인터프리터 언어
소스코드를 한 줄 한 줄 읽어가며 명령을 바로 처리하는 프로그램(언어). 번역과 실행이 동시에 이루어진다.

  • 규모가 큰 소스의 경우, 컴파일 언어는 프로그램 실행 전 먼저 기계어로 컴파일하는 데 시간이 오래 걸릴 수 있는데 인터프리터 언어는 코드를 한 번에 한 줄씩 읽어 들이면서 바로 실행이 가능함.

  • 즉, 한 줄 씩 명령을 내리다 보니, 그 명령 자체의 속도는 컴파일러 언어에 비해 느림.

  • 그래도 인터프리터 언어는 고급 프로그램을 즉시 실행시킬 수 있어 프로그램 수정이 간단함.

예시: 자바스크립트, 파이썬, 루비, sql,...

컴파일 언어
코드가 실행되기 전 컴파일러를 거쳐서 기계어로 모두 변환되어 실행되는 프로그래밍 언어이다.

  • 한 줄씩 명령을 내리는 게 아니라, 명령들을 다 모아놓고 한 번에 실행하는 방식임. 코드를 다 적고 나서 한꺼번에 검사하는 형태라고 보면 됨.

  • 규모가 큰 소스의 경우, 컴파일된 프로그램의 경우 일반적으로 인터프리터를 이용해 실행시키는 것보다 훨씬 빠르게 동작 하나, 컴파일 과정에서는 시간이 상당히 많이 소요되고 메모리도 많이 차지함.

  • 소스코드를 기계어로 번역하는 과정이 빌드 과정. 고급언어 ---(변환)--> 로우 레벨 언어(기계어) 이런 빌드 과정을 거쳐서 실행파일을 생성함

예시: C, C++, C#, Go,...

# 프로그래밍 언어도 또 다른 프로그램이다.
# 컴파일러: compiler, 특정 프로그래밍 언어를 다른 프로그래밍 언어로 옮기는 프로그램

자바 같은 경우에는 자바 컴파일러와 자바 인터프리터가 있음. 자바 컴파일러는 .java라고 쓰인 소스 파일을 .class 파일로 변환해줌. 자바 인터프리터는 바이트코드로 작성된 클래스 파일을 특정 환경의 기계에서 실행될 수 있도록 기계어로 변환해줌. 그래서 자바는 컴파일 언어라고만 볼 수 없음

c 빌드 배포

sudo apt update
sudo apt install gcc -y

# 호스트명 바꾸기
sudo hostnamectl set-hostname mybuild-ub
exec bash

# gcc 실행파일 만들기
gcc hello.c -o hello
  • ELF: 실행파일임을 확인
file hello

# 실행
./hello

도커 설치

우분투에 도커 설치 참고 사이트

sudo apt-get install -y \
    ca-certificates \
    curl \
    gnupg \
    lsb-release


# Docker의 공식 GPG 키를 추가    
sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# 리포지토리를 설정
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  
# 우분투의 저장소 파일 확인
sudo cat /etc/apt/sources.list.d/docker.list 

# 저장소를 추가 했기 때문에 다시 업데이트
sudo apt-get update

# 패키지 설치
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 일일이 sudo 안쓰고 관리자 권한으로 실행, 그룹 부여
sudo usermod -aG docker $USER
exec bash
docker ps

# 재접속 시 도커 명령어 사용 가능
exit
docker ps
  • 도커는 루트사용자만 작성 가능하기 때문에 도커그룹에 사용자를 포함 시켜줘야함

docker 이미지 생성

docker image pull ubuntu:focal

vi Dockerfile 

FROM ubuntu:focal  # 이미지 다운 
COPY hello /usr/local/bin/  # hello실행파일을 디렉터리에 복사
CMD ["/usr/local/bin/hello"]  # 해당 디렉터리에서 복사된 hello실행파일 실행 

# 빌드
docker image build -t hello-c .

# 실행
docker container run helllo-c
  • CMD가 원래 /bin/bash 였던 것을 위 디렉터리로 바꿔준다.

  • image가 매우 용량이 크기 때문에 hello world 한문장 실행시키는데 용량이 매우크기 때문에 매우 비효율적이다.

  • 이미지를 scratch: 빈 이미지를 사용하면 용량이 16.7KB로 다른 것보다 사이즈가 작아 합리적인것을 볼 수 있다.

하지만 실행이 되지않는다. 왜냐하면 hello라는 파일이 혼자 동작하지 못하기 때문이다. file로 확인해보면 dynamically-linked되었다고 나오는데 밑 명령어로 이 파일을 실행하는데 필요한 것들을 확인할 수 있다.

# libc는 c를 실행시켜줄 수 있는 라이브러리이다.
ldd hello
  • --static옵션을 붙여서 만들면 file로 확인 했을떄 statically-linked라고 나온다. 이는 이 파일을 실행하는데 필요한 라이브러리를 파일안에 집어넣어서 실행파일로 만든 것이다.
    도커환경에서는 static으로 구성하는 것이 필요한 것만 넣을 수 있기 때문에 이미지를 만드는 경우에 효율적이다.
# dynamic link
gcc hello.c -o hello 

# static link
gcc hello.c -o hello-static --static 

file 명령어를 통해 파일이 dynamic link인지, static link인지를 알 수 있다.

  • 도커 파일로 이미지 생성, 정상적으로 run됨
vi Dockerfile 

FROM scratch
COPY hello-static /hello-static  # static 실행파일 사용 
CMD ["/hello-static"] 

# 빌드
docker image build -t hello-c:static .

# 실행
docker container run helllo-c:static

statically-linked VS dynamically-linked

예를들어 웹과 메신저가 있다고 가정하자. 각각은 TCP/IP통신을 해야한다. 각각 TCP/IP통신을 하기위한 라이브러리를 각각 독자적으로 가지고 있는 것이 Static link 이다. 하지만 각각 라이브러리를 가지고 있다면 용량이 계속해서 늘어날 것이고 매우 비효율적이다.

따라서 이 경우에는 dynamic link 를 통해 TCP/IP 라이브러리를 외부에 놓고 메신저와 웹이 해당 라이브러리를 참조만 하는 형태로 구성한다.

  • 하지만 Docker image생성에서는 현재 Static link를 사용하는 추세이다. 이유는, 필요한 라이브러리만 사용할 수 있기 때문에 오히려 이미지 측면에서는 용량을 줄일 수 있다.

지금까지는 직접 gcc를 통해 빌드를 하였고 그다음에 이미지로 만들었다.

Dockerfile - Multi-stage build(멀티스테이지 빌드)

맨 처음에 gcc:12버전인 컨테이너를 만듬, 현재 디렉터리에 있는 호스트의 소스코드인 hello.c파일을 컨테이너 내부에 복사, gcc 커맨드로 static 빌드를 실행. 이를 실행하면 최종적으로 /usr/src/hello 디렉터리 밑에 hello라는 실행파일이 만들어진다.

gcc는 빌드할때는 필요하지만 그 후는 필요하지 않다. 따라서 놔두면 용량을 엄청 잡아먹기 때문에 내부에 컨테이너를 하나 더 만들어서 빌드한 파일을 가져온다.

docker image pull gcc:12

FROM gcc:12 AS cbuilder  # 별명 지정
COPY . /usr/src/hello
WORKDIR /usr/src/hello
RUN gcc hello.c -o hello --static

FROM scratch
COPY --from=cbuilder /usr/src/hello/hello /hello  # cbuilder 컨테이너에 있는 파일 가져옴
                          source 주소    내 dest 주소
CMD ["/hello"]

# 빌드
docker iamge build -t hello-c:multi .

# 이미지 확인
docker image ls

# 실행
docker container run hello-c:multi
  • Aritifact: 인공물, 작업을 해서 만들어진 파일들을 의미, 여기서는 빌드해서 만들어진 파일을 의미한다.

golang

cd ~
wget https://go.dev/dl/go1.18.10.linux-amd64.tar.gz
tar xf go1.18.10.linux-amd64.tar.gz

# go 디렉터리 복사
sudo cp -r go /usr/local

# path변수 등록
echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc

# 다시 bashrc 팡리 읽기
source ~/.bashrc
  • go는 기본적으로 statically linked이다.
cd example-hello/golang/hello

# go파일 실행
go run hello.go 

# go파일 실행파일로 빌드
go build -o hello . 

# 실행파일 실행 
./hello 
  • 도커 이미지 파일로 실행
vi Dockerfile 

FROM scratch
COPY hello /hello
CMD ["/hello"]

docker image build -t hello-go:scratch .
docker container run hello-go:scratch 
  • multi-build 형태로 만듬
vi Dockerfile 

FROM golang:1.18 AS gobuilder
ENV CGO_ENABLED=0
COPY . /usr/src/hello/
WORKDIR /usr/src/hello
RUN go build -o hello-http .

FROM scratch
COPY --from=gobuilder /usr/src/hello/hello-http /hello-http
CMD ["/hello-http"]
EXPOSE 3000/tcp
  • go로 http 어플리케이션 만들기
cd ../hello-http

tmux
(ctrl+b) "

# 위의 창
go run hello-http.go

# 아래창
curl localhost:3000
# 포트 확인
sudo ss -tnlp

포트가 열려있기 때문에 외부에서도 인스턴스의 퍼블릭 주소를 통해 접속할 수 있는데 포트가 보안그룹에 걸려 현재는 접속을 하지 못한다. 인바운드 규칙에서 3000번 포트를 허용해주면 접속이 가능하다.

기본 빌드과정

  • 개발자가 개발을 하고 나온 코드를 add와 commit 그리고 push를 통해 remote repo에 올린다.
  • 그 코드를 pull을 통해 갖고와서 언어에 맞게 run시켜보고 build를 통해 실행파일 형태로 생성한다.
  • dockerfile을 열어 생성한 실행파일을 이용해 multi-build형태로 작성하던, static link / dynamic link를 사용하든 작성하고 , dockerfile을 build해서 이미지를 생성한다.
  • 해당 이미지를 run시켜서 실행이 잘되는지 테스트하고 최종적으로 docker-hub와 같은 repository에 올린다
  • k8s를 이용해 올라온 이미지를 이용해 배포하면 된다.

nodejs

nodejs는 엄밀히 말하면 언어는 아니다. 기본적으로는 javascript를 사용한다.

  • frontend(web browser) = js, html, css, 그림
  • nodejs는 js를 backend(서버)에서 사용하기 위해 개발
  • 비동기 처리 이벤트에 특화 되어있다.
cd example-hello/nodejs/package.json

# node package manager 설치
sudo apt install -y npm
sudo apt install -y tree

# package.json 설치, node_modules 생성 
npm install

# 어플리케이션 실행, package.json에 뭘 실행할 것인지 적혀있음.
npm start

# 실행명령으로 node hello_world.js 명령도 가능

패키지를 설치하게 되면 node_modules에 설치가 되는데 이 패키지는 git에 올리지 않는다.

tmux
(ctrl+b) "

# 위의 창
npm start

# 아래창
curl localhost:8080

인바운드 규칙에서 8080번 포트를 허용해주면 퍼블릭 주소로 접속이 가능하다.

  • 스크립트 언어들은 대부분 멀티빌드를 필요로 하지 않는다. node라는 이미지는 존재
# 이미지 다운 
docker image pull node:16 

vi Dockerfile

FROM node:16 
COPY . /usr/src/hello/
WORKDIR /usr/src/hello
RUN npm install
CMD ["npm", "start"]
EXPOSE 8080/tcp

# 이미지 빌드 
docker image build -t hello-node . 
docker image ls

# 백그라운드에서 포트포워딩 실행
docker container run -d -p 8080:8080 hello-node
docker container ls
curl localhost:8080

python

django와 flask 2가지가 있다. flask는 mircoservice를 만들때 사용하고 django는 전통적인 모노리스서비스를 만들 때 사용한다.

<모노리스(Monolith)서비스의 개요>

마이크로서비스의 반대되는 개념이 모노리스서비스이다. 기존에 운영하던 시스템은 모든 서비스가 하나의 시스템에 구현되어 있었다. 쇼핑몰을 예로 들면 정산, 판매, 입고 시스템이 하나의 서비스로 운영되는 구조이다.

모노리스서비스로 개발된 애플리케이션의 경우 시스템이 커질수록 빌드 배포가 어렵고 복잡해진다. 이러한 문제점을 해결하기 위하여 나타난 개념이 마이크로 서비스이다.

<마이크로서비스의 개요>

마이크로서비스는 애플리케이션의 구성요소를 특정 목적별로 분리한되 독립적으로 작동할 수 있는 작은 서비스로 만들고, 이 서비스들을 조합하여 완성된 애플리케이션으로 조립하는 개발형태를 말한다.
이때 각 서비스들은 API와 HTTP를 이용한 REST Api를 이용하여 연결한다.
각 서비스들은 독립적으로 존재하고 작동하며, 각자 관리 및 업데이트 된다.

쇼핑몰을 예로 들면 정산시스템은 node.js로 만들고, 판매시스템은 spring으로 만들고, 입고시스템은 django로 만들어서 각각의 서비스를 분리하고 서비스들 간의 연결은 REST Api를 이용하는 것이다.

flask

requirements.txt에서 필요한 패키지들의 목록을 확인할 수 있다.

cd example-hello/python/flask/

# python 설치 확인 및 pip설치
python3
sudo apt install -y python3-pip
pip --version
  • python개발을 할때 가상환경이 제일 중요하다. python들은 패키지 관리가 좋지 않은데 특정 어플리케이션에만 패키지를 설치 할 수 있도록 분리를 해준다.
# virtualenv 패키지 설치
python3 -m pip install --user -U virtualenv
# 명령어의 설치위치를 path변수에 등록
echo 'export PATH=$PATH:~/.local/bin' >> ~/.bashrc
source ~/.bashrc

# venv라는 가상환경을 만듬
virtualenv venv

  • 가상환경에서 패키지의 설치를 진행해줘야함
# 가상환경 들어가기
source venv/bin/activate

pip install -r requirements.txt

# 가상환경 나가기
deactivate

# flask app실행 
export FLASK_APP=hello
flask run --host=0.0.0.0 --port=5000

호스트를 지정하지 않으면 로컬 호스트에서만 접근이 가능하고 외부에서는 접근이 불가능하다.

  • 퍼블릭 IP/5000/hello or lee(이름) 등을 뒤에 추가해주면 이에 관한 내용으로 나오는 것을 확인 할 수 있다.

깃에 올릴때에는 가상환경이나 캐쉬같은 것을 제거한 이후에 올린다. 따라서 gitignore에서 .env 나 cache 등을 제외한다고 명시한다.

  • 도커이미지 생성 및 실행
vi Dockerfile

FROM python:3.8
ENV FLASK_APP=hello
COPY . /usr/src/hello/
WORKDIR /usr/src/hello
RUN pip3 install -r requirements.txt
CMD ["flask", "run", "--host=0.0.0.0", "--port=5000"] 
EXPOSE 5000/tcp

# 이미지 생성 
docker build -t hello-flask:v1 . 

# detach모드, 포트포워딩해서 run 
docker container run -d -p 5000:5000 hello-flask:v1


# 가상환경 해제
deactivate

flask 공식 이미지는 존재하지 않는다.

django

# 현재 디렉터리에 새로운 가상환경 생성
virtualenv venv 
# 가상환경 접속
source venv/bin/activate 
# 패키지 설치 
pip install -r requirements.txt 

# django app실행
python hello/manage.py runserver 0.0.0.0:8000
  • 도커 이미지 생성 및 실행
vi Dockerfile 

FROM python:3.8
COPY . /usr/src/hello/
WORKDIR /usr/src/hello
RUN pip3 install -r requirements.txt
CMD ["python3", "hello/manage.py", "runserver", "0.0.0.0:8000"]
EXPOSE 8000/tcp

# 이미지 생성 
docker build -t hello:v1 . 
# detach모드, 포트포워딩해서 run
docker container run -d -p 8000:8000 hello:v1


# 가상환경 해제
deactivate

java

  • maven: 프로젝트 관리자, 어플리케이션의 단위
  • jar: 자바 아카이브, 자바 파일을 한꺼번에 실행시켜줄 수 있도록 해줌, 이또한 웹/앱 만들때 사용하기도 함(zip 형태)
  • war: 웹 아카이브, 웹/앱 만들때 사용함
# 자바 설치 확인, 11버전이 설치되어있음
java --version
sudo apt install -y maven

maven의 build lifecycle

  • clean: 타켓 디렉터리 삭제
  • complie: 소스 코드를 빌드를 해서 파일로 만듬
  • package: 컴파일 + 테스트

  • install: maven용 로컬 저장소에 설치함
  • deploy: 인스톨하고 난 뒤 최종 웹서버에 배포를 함, but 우리는 도커 이미지를 만들 것이기 때문에 install과 deploy는 하지 않음

jar파일

# 위에서 빌드하고 테스트까지 진행함
mvn package

# 확인해보면 target이 생성된 것을 확인 가능
ls

#jar 파일 실행
java -jar target/spring-hello-0.0.1.jar
  • 도커 이미지로 생성
docker image pull maven:3-openjdk-8
docker image pull openjdk:8-jre 

# 도커파일 작성
vi Dockerfile 

FROM maven:3-openjdk-8 AS mbuilder
COPY . /usr/src/hello/
WORKDIR /usr/src/hello
RUN mvn package

FROM openjdk:8-jre   # 실행버전이기에 jre(자바 런타임 환경)를 사용
COPY --from=mbuilder /usr/src/hello/target/spring-hello-0.0.1.jar /usr/src/
CMD ["java", "-jar", "/usr/src/spring-hello-0.0.1.jar"]


# 이미지 생성
docker image build -t hello-java . 

# 실행
docker container run hello-java # run 

# 새로운 빌드를 하기위해 기존의 package삭제 
mvn clean 

maven 이미지는 사이즈가 크다.

자바 개발 키트(Java Development Kit, JDK), 자바 가상 머신(Java Virtual Machine, JVM), 자바 런타임 환경(Java Runtime Environment, JRE)은 자바 애플리케이션을 개발하고 실행하기 위한 자바 플랫폼의 3대 구성 요소다.
자바 런타임 환경의 이해

war파일

war파일은 java기반의 web-application서버가 필요하다.

  • tomcat, 웹 로직, 와일드 스피어 등
  • 주로 tomcat을 사용, 웹 컨테이너라고 하며 was라고 하는 웹 어플리케이션 서버이다.
# target 생성 
mvn package 
sudo apt install tomcat9 -y   
  • AWS의 보안그룹의 인바운드에 8080포트 추가 후 브라우저에 EC2인스턴스의 PUBLIC IP:8080 입력하면 tomcat webserver에 접속되는 것을 확인

  • tomcat의 root디렉터리인 /var/lib/tomcat9/webapps에 war파일을 복사하면 디렉터리에 있는 ROOT가 war파일을 알아서 푼다.

cd ~/example-hello/java/maven-spring-hello-webapp/target
sudo cp hello-world.war /var/lib/tomcat9/webapps/

# 복사 된 것 확인
cd /var/lib/tomcat9/webapps/
ls


-> 위에서 입력한 publicip:8080에 hello-world경로를 입력하면 웹페이지가 나오는 것을 확인
-> root 디렉터리에 ROOT가 war파일을 풀었기 때문에 hello-world가 존재하게 된다. 따라서 웹페이지가 뜨는 것이다.

  • 이 내용의 형식을 표현해주는 것은 example-hello/java/maven-spring-hello-webapp/src/main/webapp/WEB-INF/views/index.jsp에서 확인 가능
  • 도커 이미지 생성
sudo systemctl stop tomcat9

vi Dockerfile 

FROM maven:3-openjdk-8 AS mbuilder
COPY . /usr/src/hello/
WORKDIR /usr/src/hello
RUN mvn package

FROM tomcat:8-jre8
COPY --from=mbuilder /usr/src/hello/webapp/target/webapp.war /usr/local/tomcat/webapps/ROOT.war
EXPOSE 8080/tcp


systemctl stop tomcat9

# 빌드 및 실행
docker image build -t hello:war .
docker container run -d -p 8080:8080 hello:war

모든 자바 웹앱인 포트 8080을 사용하고 있기 떄문에 프로세스가 실행중이거나 tomcat9이 실행중이면 포트 충돌이 발생한다. 따라서 프로세스를 중단시키거나 tomcat을 중지시켜줘야한다.(systemctl stop tomcat9)

  • maven-spring-boot-hello-webapp은 스프링 부트로 만들어 둔 것이다. 여기서 보면 파일은 jar이지만 이 안에 자바코드랑 tomcat를 넣어두었기 때문에 실행하게 되면 웹이 실행되는 것이다. 대신 용량이 큰 단점이 있다.
cd example-hello/java/maven-spring-boot-hello-webapp/target

# 스프링부트 작동, 웹에서 서버 접속 가능
java -jar spring-webapp-0.0.1.jar
profile
전공 소개

0개의 댓글