Openshift 4.12.0 응용프로그램 배포 - 개발 언어 (PHP, Python, Go, NodeJS, Java, Ruby)
본 문서는 공식 Documents 를 참고하여 작성하였습니다.
https://docs.openshift.com/container-platform/4.12/openshift_images/create-images.html
여기에서 잘 되지 않아 헤매었는데, 현재까지 시행착오를 겪고 이해한 바로는 이렇습니다.
웹서버와 데이터베이스 Pod 는 가동 후 데이터 입력 및 설정을 추가로 하면 되는데,
개발 언어들은 이와 같은 방식으로 Pod 가동이 안됩니다.
(CrashLoopBackOff, Completed 또는 Error 발생)
개발 언어들은 두가지 (이미지 + 어플리케이션 소스 코드) 가 올바로 준비되어야 Running 상태로 가동이 됩니다.
- 이미지 : 개발 언어 이미지
- 응용프로그램 소스 : 개발 언어를 이용하여 개발한 소스 코드
웹서버와 같이 oc new-app docker.io/php 명령으로 PHP 이미지를 사용하여 새로운 Pod 를 만들면
PHP 이미지에는 실행할 소스 코드가 포함되어 있지 않기 때문에 컨테이너가 Completed 상태로 종료됩니다.
그래서 PHP 응용프로그램을 실행하려면, 이미지에 개발한 소스 코드를 포함시켜야 합니다.
본 매뉴얼에서 진행한 테스트 절차는 아래와 같습니다.
- 개발 소스 준비 > build 하여 소스코드를 이미지에 삽입 > Registry 에 등록 > yaml 작성 > 응용프로그램 배포
* 사전 이해
- docker build 명령은 Dockerfile 을 사용하여 새로운 이미지를 빌드하고, 이 이미지를 로컬에 저장합니다.
docker new-build 명령은 Dockerfile 을 사용하여 새로운 이미지를 빌드하지만, 이 이미지를 메모리에 저장합니다.
로컬 머신의 디스크 공간을 덜 사용하기 때문에, 빌드 후 즉시 이미지를 삭제할 경우 유용합니다.
- oc new-build 명령은 새로운 Docker 이미지를 처음부터 빌드하는데 사용되며,
oc start-build 명령은 이미 생성된 BuildConfig를 사용하여 새로운 빌드를 시작하는데 사용됩니다.
- Podman 은 Docker CLI 와 호환되는 CLI 와 API 를 제공하므로, 대부분의 Docker 명령어를 사용할 수 있습니다.
하지만 Podman 으로 Docker 이미지를 빌드하거나 실행하려면 Docker 데몬이 설치되어 있어야 합니다.
여기에서는 docker 로 설명 드리지만 docker, podman 패키지가 모두 설치된 상태에서는 podman 명령을 사용해도 무관합니다.
# yum -y install docker
# systemctl enable docker
# systemctl start docker
* 프레임워크
개발 언어 컨테이너 만으로는 컨테이너 포트 활성화가 되지 않아 웹서버나 프레임워크를 이용하여 포트를 활성화하고 웹페이지를 확인합니다.
PHP 는 apache 웹서버가 내장된 이미지를 사용하면 포트를 활성화 하고 PHP 코드를 실행할 수 있습니다.
나머지 각 언어들은 다음과 같은 프레임워크를 사용하여 웹 어플리케이션을 개발할 수 있습니다. 그리고 내장된 웹서버를 활용할 수 있습니다.
- PHP : php-apache 이미지
- Ruby : Ruby on Rails, Sinatra
- Python : Django, Flask
- Go : Gin, Echo
- Node.js : Express, Koa
- Java : Spring, Struts
1. PHP 배포
Openshift에서 PHP 를 배포할 때, 포트를 열고 웹페이지를 출력하려면 PHP 어플리케이션을 웹서버와 함께 실행해야 합니다.
이를 위해서는 PHP 와 웹서버가 함께 포함된 이미지를 사용해야 하며, 단순히 PHP 이미지만 사용하는 것은 불가능합니다.
Apache 웹서버와 PHP 가 함께 포함된 이미지 중에서 가장 일반적으로 사용되는 것은 php-apache 이미지입니다.
이 이미지는 Apache 웹서버와 PHP 를 설치하고 구성하여 특정포트에서 애플리케이션을 실행할 수 있도록 합니다.
진행할 예제는 php:7.4-apache 기준으로 작성하였습니다.
우선 php:7.4-apache 이미지를 다운로드 합니다.
# docker pull docker.io/php:7.4-apache
테스트용으로 PHP 소스 파일을 만듭니다.
# mkdir ./source
# vi ./source/index.php
<?php echo "Good job"; ?> |
Dockerfile 을 아래 내용으로 작성합니다.
# vi Dockerfile
FROM docker.io/php:7.4-apache # Apache 설정 #RUN a2enmod rewrite # 소스 복사 COPY ./source/ /var/www/html/ # PHP 확장기능 설치 #RUN docker-php-ext-install pdo_mysql # 기본 포트 변경 RUN sed -i 's/Listen 80/Listen 8080/' /etc/apache2/ports.conf RUN sed -i 's/Listen 443/Listen 8443/' /etc/apache2/ports.conf # Apache 서버 시작 CMD ["apache2-foreground"] |
기본포트 80, 443 등 로컬 haproxy 가 사용하는 포트는 사용하지 못하므로 8080, 8443 등으로 변경해서 사용해야 합니다.
변경하지 않고 이미지를 만들게 되면 해당 이미지로 생성한 Pod 에서 아래와 같은 에러 메세지를 볼 수 있습니다.
# oc logs <Pod 이름>
(13)Permission denied: AH00072: make_sock: could not bind to address [::]:80
(13)Permission denied: AH00072: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
AH00015: Unable to open logs
현재 디렉토리에 있는 Dockerfile 을 사용하여 Docker 이미지를 빌드 합니다.
형식) docker build --tag <새 이미지 이름> <Dockerfile 위치>
# docker build --tag my-app5 .
새로운 이미지가 확인 되었습니다.
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-app5 latest b0aeb2e3c8af 42 seconds ago 453 MB
docker.io/php 7.4-apache 20a3732f422b 4 months ago 453 MB
새로 만든 PHP 이미지로 응용프로그램을 생성하고 상태를 확인합니다.
형식) oc new-app --image=<이미지 이름> --name=<Pod 이름 정하고 싶은 경우>
# oc new-app --image=my-app5
# oc get pods
NAME READY STATUS RESTARTS AGE
my-app5-64b9b569fb-jlw5h 0/1 ImagePullBackOff 0 8s
* 에러 발생 이유
Pod 상태가 ErrImagePull / 자동 재시도 (또는 ImagePullBackOff / 완전 실패) 일 경우는 이미지가 존재하지 않거나 액세스 권한이 없는 경우 발생할 수 있습니다. 이러한 문제를 해결하려면, 이미지가 올바르게 빌드되어 이미지 레지스트리에 올바르게 업로드되었는지 확인하고, 이미지 레지스트리에 액세스 할 수 있는 권한을 가지고 있는지 확인해야 합니다.
그렇지만 저의 경우는 아래와 같았으며, 다른 방법으로 해결하였습니다.
로그를 살펴봅니다.
에러1
# oc logs my-app5-64b9b569fb-jlw5h
Error from server: Get "https://115.68.142.105:10250/containerLogs/project412/my-app5-64b9b569fb-jlw5h/my-app5": remote error: tls: internal error
위 로그는 TLS 연결 오류입니다. 만약 Docker가 로컬 호스트에 있는 경우, 인증서와 관련된 문제는 발생하지 않을 수 있습니다.
하지만 OpenShift 클러스터는 기본적으로 Docker 데몬을 직접 실행하지 않으므로, 로컬 호스트의 Docker 데몬에 이미지가 존재하는 경우에도 OpenShift 클러스터와 Docker 레지스트리 간의 연결 문제가 발생할 수 있습니다.
이 경우, oc new-app 명령어에서 Docker 이미지를 로컬 호스트의 Docker 데몬에서 가져오는 대신, OpenShift 클러스터 내에서 Docker 이미지를 빌드 하도록 지시할 수 있습니다. Dockerfile을 작성하고 oc new-build 명령어를 사용하여 Docker 이미지를 빌드해야 합니다.
[첫번째 방법]
다음과 같이 oc new-build 명령어를 사용하여 Dockerfile 을 빌드하면 됩니다.
형식) oc new-build --name=<app 이름> --binary --strategy=docker
# oc new-build --name=my-app --binary --strategy=docker
형식) oc start-build <app 이름> --from-dir=<Dockerfile 위치>
# oc start-build my-app --from-dir=.
형식) oc new-app <app 이름>
# oc new-app my-app
oc new-build 명령어는 이전에 생성된 Docker 이미지를 빌드하기 위한 빈 이미지 스트림을 만듭니다.
oc start-build 명령어는 이 이미지 스트림을 사용하여 Docker 이미지를 빌드합니다.
oc new-app 명령어는 이미지 배포 명령입니다.
[두번째 방법]
또는 아래와 같이 단순화 할 수 있습니다.
# oc import-image my-php:7.4-apache --from=docker.io/php:7.4-apache --confirm
이 명령어는 Docker Hub의 docker.io/php:7.4-apache 이미지를 가져와서 OpenShift 클러스터의 my-php:7.4-apache 이미지스트림으로 변환합니다. --confirm 옵션을 사용하면, 이미지스트림이 이미 존재할 경우 덮어쓰기를 확인하는 메시지가 표시됩니다.
이미지스트림으로 변환한 후에는, oc new-app 명령어에서 이미지스트림을 사용하여 애플리케이션을 배포할 수 있습니다.
형식) oc new-app <이미지스트림 이름>~<소스 위치>
# oc new-app my-php:7.4-apache~./source/
이 명령어는 my-php:7.4-apache 이미지스트림을 사용하여 새로운 애플리케이션을 생성하고, <path-to-source-code> 경로에 있는 소스 코드와 결합하여 배포합니다. (하지만 Dockerfile 추가 옵션 사용 불가)
에러2
# oc logs my-app11-6c7c9dcd7c-z6cww
Error from server (BadRequest): container "my-app11" in pod "my-app11-6c7c9dcd7c-z6cww" is waiting to start: trying and failing to pull image
위에러를 자세히 보기 위해 oc describe 명령을 사용합니다.
# oc describe pod my-app11-6c7c9dcd7c-z6cww
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 16m default-scheduler Successfully assigned deploy/my-app11-6c7c9dcd7c-z6cww to worker01.az1.sysdocu.kr
Normal AddedInterface 16m multus Add eth0 [10.128.2.44/23] from openshift-sdn
Normal Pulling 14m (x4 over 16m) kubelet Pulling image "my-app11:latest"
Warning Failed 14m (x4 over 16m) kubelet Failed to pull image "my-app5:latest": rpc error: code = Unknown desc = reading manifest latest in docker.io/library/my-app5: errors: denied: requested access to the resource is denied
unauthorized: authentication required
Warning Failed 14m (x4 over 16m) kubelet Error: ErrImagePull
Warning Failed 14m (x6 over 16m) kubelet Error: ImagePullBackOff
Normal BackOff 69s (x63 over 16m) kubelet Back-off pulling image "my-app5:latest"
권한이 없어 인증이 필요하다는 것을 확인하였습니다.
이 문제는 아래와 같이 secret 을 생성해서 프로젝트에 권한을 부여하면 됩니다.
# oc create secret docker-registry docker-registry-login \
--docker-server=115.68.142.99:5000 \
--docker-username=sysdocu \
--docker-password=12345678 \
--namespace=project412
secret/docker-registry-login created
- 115.68.142.99 : 본 서버 (OCP 서버 IP)
# oc get secrets
NAME TYPE DATA AGE
builder-dockercfg-4jj5l kubernetes.io/dockercfg 1 24h
builder-token-tpbk6 kubernetes.io/service-account-token 4 24h
default-dockercfg-hsnzx kubernetes.io/dockercfg 1 24h
default-token-fps64 kubernetes.io/service-account-token 4 24h
deployer-dockercfg-l24hl kubernetes.io/dockercfg 1 24h
deployer-token-wfrt8 kubernetes.io/service-account-token 4 24h
docker-registry-login kubernetes.io/dockerconfigjson 1 35s
secret 생성을 확인하였습니다.
이제 해당 secret 정보를 deployment, service 에 적용하고 재생성 명령을 통해 Pod 를 가동해 보겠습니다.
기존 에러 발생한 deployment, service 설정 상태를 yaml 파일로 추출합니다.
# oc get deployment my-app5 -o yaml > deployment_my-app5.yaml
# oc get service my-app5 -o yaml > service_my-app5.yaml
파일을 열어 아래 옵션에 secret 정보를 입력합니다.
아래 위치에 옵션이 없으므로 옵션명과 secret 이름을 추가해 줍니다.
# vi deployment_my-app5.yaml
... spec: template: spec: imagePullSecrets: - name: docker-registry-login ... |
# vi service_my-app5.yaml
... ... |
적용
# oc apply -f deployment_my-app11.yaml
Deployment 와 Service 를 모두 수정한 후, oc rollout 명령어를 사용하여 Pod를 재생성합니다.
# oc rollout latest deployment/my-app5
Openshift 에서 응용프로그램에 대한 라우트를 생성합니다.
# oc expose service my-app5 --port=8080
# oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
my-app5 my-app5-project412.apps.az1.sysdocu.kr php-apache 8080-tcp None
이제 웹 브라우저를 열고 route 정보에 출력된 호스트네임과 포트로 접근하면 개발한 소스 코드가 출력됩니다.
# curl my-app5-project412.apps.az1.sysdocu.kr:8080
Good job
2. Python 배포
최신 버전의 python 이미지를 다운로드 합니다.
# docker pull docker.io/python:latest
빌드할때 필요한 파일을 미리 준비해 둡니다.
# mkdir ./source
# vi ./source/server.py
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello, World!" if __name__ == "__main__": app.run(host="0.0.0.0", port=8080) |
모듈리스트를 작성한 파일을 생성합니다.
여러개의 필요한 모듈을 올릴 수 있지만 본 예제에서는 Flask 모듈만 올렸는데, Flask 프레임워크를 설치하여 웹서버 용도로 사용하기 위해서 입니다.
# vi source/requirements.txt
Flask |
빌드하기 위해 Dockerfile 을 작성합니다.
# vi Dockerfile
FROM docker.io/python:latest COPY ./source/ /app/ WORKDIR /app RUN pip install -r requirements.txt EXPOSE 8080 CMD ["python", "server.py"] |
* 설명
FROM python:latest : Python 의 최신 버전을 기반으로 하는 Docker 이미지를 사용합니다.
COPY ./source/ /app/ : 호스트의 ./source/ 디렉토리에 있는 파일들을 컨테이너 내부의 /app/ 디렉토리로 복사합니다.
WORKDIR /app : 이후 명령어들이 실행될 디렉토리를 /app 으로 설정합니다.
RUN pip install -r requirements.txt : 파일에 작성되어있는 모든 모듈을 설치합니다.
EXPOSE 8080 : 호스트의 8080 포트와 컨테이너 내부의 8080 포트를 연결합니다.
CMD ["python", "server.py"] : 컨테이너가 시작될 때 /app 디렉토리에서 server.py 를 찾아 python 명령으로 실행합니다.
빌드 실행하면 새로운 도커 이미지가 생성됩니다.
# docker build -t python-app .
# docker images |grep python-app
python-app latest fc6586828737 5 seconds ago 921 MB
로컬의 도커 이미지를 Registry 에 올려놓고 사용하도록 합니다.
업로드 권한이 필요하므로 생성했던 secret 계정을 이용해 Registry 에 로그인을 합니다.
# docker login -u sysdocu -p 12345678 default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000
Login Succeeded
태그 설정을 하고 이미지를 업로드 합니다.
# docker tag python-app default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/python
# docker push default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/python
python 응용프로그램을 배포하기 위해 yaml 파일을 작성합니다.
# vi app.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: python-sample spec: replicas: 1 selector: matchLabels: app: python-sample template: metadata: labels: app: python-sample spec: containers: - name: python-sample image: default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/python ports: - containerPort: 8080 protocol: TCP imagePullSecrets: - name: sysdocu --- apiVersion: v1 kind: Service metadata: name: python-sample spec: type: LoadBalancer selector: app: python-sample ports: - name: python-sample protocol: TCP port: 8080 targetPort: 8080 |
위 yaml 파일을 적용면 deployment, pod, service, endpoint 가 모두 생성됩니다.
# oc apply -f app.yaml
deployment.apps/python-sample created
service/python-sample created
추가로 외부에서 접근이 가능하도록 route 를 생성합니다.
# oc expose service python-sample --name=python-sample --port=8080
route.route.openshift.io/python-sample exposed
생성된 route 정보를 확인합니다.
# oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
python-sample python-sample-project412.apps.az1.sysdocu.kr python-sample 8080 None
외부에서 호스트명을 이용해 컨테이너 접근이 가능합니다.
# curl python-sample-project412.apps.az1.sysdocu.kr
Hello, World!
3. Go (Golang) 배포
최신 버전의 go 이미지를 다운로드 합니다.
# docker pull docker.io/golang:latest
빌드할때 필요한 파일을 미리 준비해 둡니다.
# mkdir ./source
# vi ./source/gogin.go
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { c.String(200, "Hello, world!") }) r.Run() // default port is 8080 } |
빌드하기 위해 Dockerfile 을 작성합니다.
# vi Dockerfile
FROM golang:latest ENV CGO_ENABLED 0 COPY ./source/ /app/ WORKDIR /app RUN go mod init github.com/example/example RUN go mod tidy RUN go build -o gogin . EXPOSE 8080 CMD ["/app/gogin"] |
* 설명
FROM python:latest : Golang 의 최신 버전을 기반으로 하는 Docker 이미지를 사용합니다.
ENV CGO_ENABLED 0 : 환경 변수를 설정합니다. CGO_ENABLED 를 0 으로 설정하여 C 기반 라이브러리를 사용하지 않도록 합니다.
COPY ./source/ /app/ : 호스트의 ./source/ 디렉토리에 있는 파일들을 컨테이너 내부의 /app/ 디렉토리로 복사합니다.
WORKDIR /app : 이후 명령어들이 실행될 디렉토리를 /app 으로 설정합니다.
RUN go mod init github.com/example/example : 모듈을 초기화 할때 사용하는 명령이지만 잘못된 URL 을 입력해도 넘어갑니다.
이 행이 있어야 다음 행으로 넘어갈 수 있어서 아무렇게나 입력하였습니다.
RUN go mod tidy : Go 모듈 시스템을 사용하여 의존성을 관리하고 go.mod 및 go.sum 파일을 업데이트합니다.
RUN go build -o gogin . : /app 디렉토리에 위치한 소스코드를 컴파일하여 gogin 바이너리 파일을 생성하는 명령입니다.
EXPOSE 8080 : 컨테이너가 사용할 포트 번호를 설정합니다. 이 경우, 8080번 포트를 사용합니다.
CMD ["/app/gogin"] : 컨테이너가 시작될 때 실행할 명령을 설정합니다. 이 경우, gogin 실행 파일을 실행하여 Go 애플리케이션을 실행합니다.
빌드 실행하면 새로운 도커 이미지가 생성됩니다.
# docker build -t go-app .
# docker images |grep go-app
go-app latest f5e7faecac07 3 minutes ago 1.05 GB
로컬의 도커 이미지를 Registry 에 올려놓고 사용하도록 합니다.
# docker tag go-app default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/go
# docker push default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/go
Go 응용프로그램을 배포하기 위해 yaml 파일을 작성합니다.
# vi app.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: go-sample spec: replicas: 1 selector: matchLabels: app: go-sample template: metadata: labels: app: go-sample spec: containers: - name: go-sample image: default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/go ports: - containerPort: 8080 protocol: TCP imagePullSecrets: - name: sysdocu --- apiVersion: v1 kind: Service metadata: name: go-sample spec: type: LoadBalancer selector: app: go-sample ports: - name: go-sample protocol: TCP port: 8080 targetPort: 8080 |
위 yaml 파일을 적용면 deployment, pod, service, endpoint 가 모두 생성됩니다.
# oc apply -f app.yaml
deployment.apps/go-sample created
service/go-sample created
추가로 외부에서 접근이 가능하도록 route 를 생성합니다.
# oc expose service go-sample --name=go-sample --port=8080
route.route.openshift.io/go-sample exposed
생성된 route 정보를 확인합니다.
# oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
go-sample go-sample-project412.apps.az1.sysdocu.kr go-sample 8888 None
python-sample python-sample-project412.apps.az1.sysdocu.kr python-sample 8080 None
외부에서 호스트명을 이용해 컨테이너 접근이 가능합니다.
# curl go-sample-project412.apps.az1.sysdocu.kr
Hello, world!
4. NodeJS 배포
최신 버전의 nodejs 이미지를 다운로드 합니다.
# docker pull docker.io/node:latest
빌드할때 필요한 파일을 미리 준비해 둡니다.
# mkdir ./source
# vi ./source/index.js
var express = require('express') var app = express() app.get('/', function (req, res) { res.send('Hello World!') }) app.listen(8080, function () { console.log('app listening on port 8080!') }) |
# vi ./source/package.json
{ "name": "node-sample", "version": "1.0.0", "description": "A simple Node.js application", "main": "index.js", "scripts": { "start": "node index.js" }, "dependencies": { "express": "^4.17.2" } } |
빌드하기 위해 Dockerfile 을 작성합니다.
# vi Dockerfile
FROM node:latest COPY ./source/ /app/ WORKDIR /app RUN npm init RUN npm install express EXPOSE 8080 CMD ["npm", "start"] |
* 설명
FROM python:latest : Golang 의 최신 버전을 기반으로 하는 Docker 이미지를 사용합니다.
COPY ./source/ /app/ : 호스트의 ./source/ 디렉토리에 있는 파일들을 컨테이너 내부의 /app/ 디렉토리로 복사합니다.
WORKDIR /app : 이후 명령어들이 실행될 디렉토리를 /app 으로 설정합니다.
RUN npm init -y : Node.js 프로젝트를 위한 package.json 파일을 생성합니다. 상호 작용모드로 진입하지 않도록 -y 옵션을 추가합니다.
RUN npm install express : Node.js 프로젝트에서 express 모듈을 설치합니다.
EXPOSE 8080 : 컨테이너가 사용할 포트 번호를 설정합니다.
CMD ["npm", "start"] : 컨테이너가 시작될 때 실행할 명령을 설정합니다. 여기서는 npm start를 실행합니다.
이는 Node.js 프로젝트 내부의 package.json 파일에서 start 스크립트를 실행합니다.
빌드 실행하면 새로운 도커 이미지가 생성됩니다.
# docker build -t node-app .
# docker images |grep node-app
node-app latest f5e7faecac07 3 minutes ago 1.05 GB
로컬의 도커 이미지를 Registry 에 올려놓고 사용하도록 합니다.
# docker tag node-app default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/node
# docker push default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/node
nodejs 응용프로그램을 배포하기 위해 yaml 파일을 작성합니다.
# vi app.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: node-sample spec: replicas: 1 selector: matchLabels: app: node-sample template: metadata: labels: app: node-sample spec: containers: - name: node-sample image: default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/node ports: - containerPort: 8080 protocol: TCP imagePullSecrets: - name: sysdocu --- apiVersion: v1 kind: Service metadata: name: node-sample spec: type: LoadBalancer selector: app: node-sample ports: - name: node-sample protocol: TCP port: 8080 targetPort: 8080 |
위 yaml 파일을 적용면 deployment, pod, service, endpoint 가 모두 생성됩니다.
# oc apply -f app.yaml
deployment.apps/node-sample created
service/node-sample created
추가로 외부에서 접근이 가능하도록 route 를 생성합니다.
# oc expose service node-sample --name=node-sample --port=8080
route.route.openshift.io/node-sample exposed
생성된 route 정보를 확인합니다.
# oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
go-sample go-sample-project412.apps.az1.sysdocu.kr go-sample 8888 None
node-sample node-sample-project412.apps.az1.sysdocu.kr node-sample 8080 None
python-sample python-sample-project412.apps.az1.sysdocu.kr python-sample 8080 None
외부에서 호스트명을 이용해 컨테이너 접근이 가능합니다.
# curl node-sample-project412.apps.az1.sysdocu.kr
Hello World!
5. Java 배포
특정 버전의 alpine (경량) java 이미지를 다운로드 합니다.
# docker pull docker.io/openjdk:17-alpine
빌드할때 필요한 파일을 미리 준비해 둡니다.
아래 java 소스를 보면 웹서버를 담당하는 것은 JDK 에서 제공하는 HTTP 서버 API 입니다.
이 API 를 사용하면 간단한 웹 서버를 만들 수 있지만, Spring 과 같은 프레임워크에 비해 제공하는 기능이 제한적입니다.
# mkdir ./source
# vi ./source/HelloWorld.java
import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; public class HelloWorld { public static void main(String[] args) throws Exception { System.out.println("Hello. Simple Web Server."); HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0); server.createContext("/", indexHandler); server.createContext("/hello", mHelloHandler); server.setExecutor(null); server.start(); } private static HttpHandler indexHandler = new HttpHandler() { @Override public void handle(HttpExchange httpExchange) throws IOException { byte[] response = "This is an index page.".getBytes(); httpExchange.sendResponseHeaders(200, response.length); OutputStream os = httpExchange.getResponseBody(); os.write(response); os.close(); } }; private static HttpHandler mHelloHandler = new HttpHandler() { @Override public void handle(HttpExchange httpExchange) throws IOException { byte[] response = "Hello. SnowDeer!!".getBytes(); httpExchange.sendResponseHeaders(200, response.length); OutputStream os = httpExchange.getResponseBody(); os.write(response); os.close(); } }; } |
* 참고
- HelloWorld.java 출처 : https://github.com/snowdeer/openshift-java-sample/blob/master/app/SimpleWebServer.java
본 매뉴얼에서는 출처의 내용중 클래스 이름을 변경하여 적용하였음
빌드하기 위해 Dockerfile 을 작성합니다.
# vi Dockerfile
FROM openjdk:latest COPY ./source/ /app/ WORKDIR /app EXPOSE 8080 RUN javac HelloWorld.java CMD ["java", "HelloWorld"] |
* 설명
FROM python:latest : Golang 의 최신 버전을 기반으로 하는 Docker 이미지를 사용합니다.
COPY ./source/ /app/ : 호스트의 ./source/ 디렉토리에 있는 파일들을 컨테이너 내부의 /app/ 디렉토리로 복사합니다.
WORKDIR /app : 이후 명령어들이 실행될 디렉토리를 /app 으로 설정합니다.
EXPOSE 8080 : 컨테이너가 사용할 포트 번호를 설정합니다.
RUN javac HelloWorld.java : HelloWorld.java 소스 파일을 컴파일 하여 실행 가능한 파일을 생성합니다.
CMD ["java", "HelloWorld"] : 컨테이너가 시작될 때 실행할 명령을 설정합니다. 여기서는 java 명령으로 HelloWorld 파일을 실행합니다.
빌드 실행하면 새로운 도커 이미지가 생성됩니다.
# docker build -t java-app .
# docker images |grep java-app
node-app latest 928efdd8d2cb 2 minutes ago 326 MB
로컬의 도커 이미지를 Registry 에 올려놓고 사용하도록 합니다.
# docker tag java-app default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/java
# docker push default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/java
java 응용프로그램을 배포하기 위해 yaml 파일을 작성합니다.
# vi app.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: java-sample spec: replicas: 1 selector: matchLabels: app: java-sample template: metadata: labels: app: java-sample spec: containers: - name: java-sample image: default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/java ports: - containerPort: 8080 protocol: TCP imagePullSecrets: - name: sysdocu --- apiVersion: v1 kind: Service metadata: name: java-sample spec: type: LoadBalancer selector: app: java-sample ports: - name: java-sample protocol: TCP port: 8080 targetPort: 8080 |
위 yaml 파일을 적용면 deployment, pod, service, endpoint 가 모두 생성됩니다.
# oc apply -f app.yaml
deployment.apps/java-sample created
service/java-sample created
추가로 외부에서 접근이 가능하도록 route 를 생성합니다.
# oc expose service java-sample --name=java-sample --port=8080
route.route.openshift.io/java-sample exposed
생성된 route 정보를 확인합니다.
# oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
go-sample go-sample-project412.apps.az1.sysdocu.kr go-sample 8888 None
java-sample java-sample-project412.apps.az1.sysdocu.kr java-sample 8080 None
nodejs-sample nodejs-sample-project412.apps.az1.sysdocu.kr nodejs-sample 8080 None
python-sample python-sample-project412.apps.az1.sysdocu.kr python-sample 8080 None
외부에서 호스트명을 이용해 컨테이너 접근이 가능합니다.
# curl java-sample-project412.apps.az1.sysdocu.kr
This is an index page.
6. Ruby 배포
빌드할때 필요한 Gemfile, config.ru 파일을 미리 준비해 둡니다.
# mkdir ./source
# vi ./source/Gemfile
source 'https://rubygems.org' gem 'rack' gem 'puma' |
# vi ./source/config.ru
require 'rack/lobster' map '/health' do health = proc do |env| [200, { "Content-Type" => "text/html" }, ["1"]] end run health end map '/lobster' do run Rack::Lobster.new end map '/headers' do headers = proc do |env| [200, { "Content-Type" => "text/plain" }, [ env.select {|key,val| key.start_with? 'HTTP_'} .collect {|key, val| [key.sub(/^HTTP_/, ''), val]} .collect {|key, val| "#{key}: #{val}"} .sort .join("\n") ]] end run headers end map '/' do welcome = proc do |env| [200, { "Content-Type" => "text/html" }, [<<WELCOME_CONTENTS <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Welcome to OpenShift</title> <style> /*! * Bootstrap v3.0.0 * * Copyright 2013 Twitter, Inc * Licensed under the Apache License v2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world @twitter by @mdo and @fat. */ .logo { background-size: cover; height: 58px; width: 180px; margin-top: 6px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkwAIYBQAAeQF6gEsgPwAAAABJRU5ErkJggg==); } .logo a { display: block; width: 100%; height: 100%; } *, *:before, *:after { -moz-box-sizing: border-box; box-sizing: border-box; } aside, footer, header, hgroup, section{ display: block; } body { color: #404040; font-family: "Helvetica Neue",Helvetica,"Liberation Sans",Arial,sans-serif; font-size: 14px; line-height: 1.4; } html { font-family: sans-serif; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; } ul { margin-top: 0; } .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px; } .container:before, .container:after { content: " "; /* 1 */ display: table; /* 2 */ } .container:after { clear: both; } .row { margin-left: -15px; margin-right: -15px; } .row:before, .row:after { content: " "; /* 1 */ display: table; /* 2 */ } .row:after { clear: both; } .col-sm-6, .col-md-6, .col-xs-12 { position: relative; min-height: 1px; padding-left: 15px; padding-right: 15px; } .col-xs-12 { width: 100%; } @media (min-width: 768px) { .container { width: 750px; } .col-sm-6 { float: left; } .col-sm-6 { width: 50%; } } @media (min-width: 992px) { .container { width: 970px; } .col-md-6 { float: left; } .col-md-6 { width: 50%; } } @media (min-width: 1200px) { .container { width: 1170px; } } a { color: #069; text-decoration: none; } a:hover { color: #EA0011; text-decoration: underline; } hgroup { margin-top: 50px; } footer { margin: 50px 0 25px; } h1, h2, h3 { color: #000; line-height: 1.38em; margin: 1.5em 0 .3em; } h1 { font-size: 25px; font-weight: 300; border-bottom: 1px solid #fff; margin-bottom: .5em; } h1:after { content: ""; display: block; width: 100%; height: 1px; background-color: #ddd; } h2 { font-size: 19px; font-weight: 400; } h3 { font-size: 15px; font-weight: 400; margin: 0 0 .3em; } p { margin: 0 0 2em; text-align: justify; } p + h2 { margin-top: 2em; } html { background: #f5f5f5; height: 100%; } code { background-color: white; border: 1px solid #ccc; padding: 1px 5px; color: #888; } pre { display: block; padding: 13.333px 20px; margin: 0 0 20px; font-size: 13px; line-height: 1.4; background-color: #fff; border-left: 2px solid rgba(120,120,120,0.35); white-space: pre; white-space: pre-wrap; word-break: normal; word-wrap: break-word; overflow: auto; font-family: Menlo,Monaco,"Liberation Mono",Consolas,monospace !important; } </style> </head> <body> <section class='container'> <hgroup> <h1>Welcome to your Ruby application on OpenShift</h1> </hgroup> <div class="row"> <section class='col-xs-12 col-sm-6 col-md-6'> <section> <h2>Deploying code changes</h2> <p> The source code for this application is available to be forked from the <a href="https://www.github.com/sclorg/ruby-ex">OpenShift GitHub repository</a>. You can configure a webhook in your repository to make OpenShift automatically start a build whenever you push your code: </p> <ol> <li>From the Web Console homepage, navigate to your project</li> <li>Click on Browse > Builds</li> <li>From the view for your Build click on the button to copy your GitHub webhook</li> <li>Navigate to your repository on GitHub and click on repository settings > webhooks</li> <li>Paste your webhook URL provided by OpenShift — that's it!</li> </ol> <p>After you save your webhook, if you refresh your settings page you can see the status of the ping that Github sent to OpenShift to verify it can reach the server.</p> <p>Note: adding a webhook requires your OpenShift server to be reachable from GitHub.</p> <h3>Working in your local Git repository</h3> <p>If you forked the application from the OpenShift GitHub example, you'll need to manually clone the repository to your local system. Copy the application's source code Git URL and then run:</p> <pre>$ git clone <git_url> <directory_to_create> # Within your project directory # Commit your changes and push to OpenShift $ git commit -a -m 'Some commit message' $ git push</pre> <p>After pushing changes, you'll need to manually trigger a build if you did not setup a webhook as described above.</p> </section> </section> <section class="col-xs-12 col-sm-6 col-md-6"> <h2>Managing your application</h2> <p>Documentation on how to manage your application from the Web Console or Command Line is available at the <a href="http://docs.okd.io/latest/dev_guide/overview.html">Developer Guide</a>.</p> <h3>Web Console</h3> <p>You can use the Web Console to view the state of your application components and launch new builds.</p> <h3>Command Line</h3> <p>With the <a href="http://docs.okd.io/latest/cli_reference/overview.html">OpenShift command line interface</a> (CLI), you can create applications and manage projects from a terminal.</p> <h2>Development Resources</h2> <ul> <li><a href="http://docs.okd.io/latest/welcome/index.html">OpenShift Documentation</a></li> <li><a href="https://github.com/openshift/origin">Openshift Origin GitHub</a></li> <li><a href="https://github.com/openshift/source-to-image">Source To Image GitHub</a></li> <li><a href="http://docs.okd.io/latest/using_images/s2i_images/ruby.html">Getting Started with Ruby on OpenShift</a></li> <li><a href="http://stackoverflow.com/questions/tagged/openshift">Stack Overflow questions for OpenShift</a></li> <li><a href="http://git-scm.com/documentation">Git documentation</a></li> </ul> </section> </div> <footer> <div class="logo"><a href="https://www.openshift.com/"></a></div> </footer> </section> </body> </html> WELCOME_CONTENTS ]] end run welcome end |
* 참고
Ruby 의 경우 Rack 애플리케이션을 실행하는 데 사용되는 파일 이름이 config.ru 입니다.
Rack 은 Ruby 웹 프레임워크에서 사용되는 인터페이스로써 HTTP 요청을 처리하고 응답을 생성하기 위한 메소드들을 제공합니다.
따라서 Ruby 애플리케이션을 컨테이너에서 실행할 때, 컨테이너는 config.ru 파일을 찾아서 Rack 애플리케이션을 실행합니다.
- config.ru 출처 : https://github.com/sclorg/ruby-ex/blob/master/config.ru
본 매뉴얼에서는 출처의 내용중 background-image 부분만 짧은 (용량이 적은) 내용으로 변경하여 적용하였음
빌드하기 위해 Dockerfile 을 작성합니다.
Ruby 에서 사용하는 프레임워크는 sinatra 를 선택하였고 아래 예제는 sinatra 설치를 포함하고 있습니다.
# vi Dockerfile
FROM ruby:latest COPY ./source/ /app/ WORKDIR /app RUN bundle install RUN gem install rack RUN gem install sinatra -v 3.0.6 EXPOSE 8080 CMD ["rackup", "-o", "0.0.0.0", "-p", "8080"] |
* 설명
FROM ruby:latest : Ruby의 최신 버전을 기반으로 하는 Docker 이미지를 사용합니다.
COPY ./source/ /app/ : 호스트의 ./source/ 디렉토리에 있는 파일들을 컨테이너 내부의 /app/ 디렉토리로 복사합니다.
WORKDIR /app : 이후 명령어들이 실행될 디렉토리를 /app 으로 설정합니다.
RUN bundle install : /app 디렉토리에서 Gemfile 에 명시된 Ruby 패키지를 설치합니다.
RUN gem install rack : Rack 웹 서버를 사용하기 위한 Ruby 패키지 rack 을 설치합니다.
RUN gem install sinatra -v 3.0.6 : Sinatra 웹 프레임워크의 특정 버전인 3.0.6 을 설치합니다.
Sinatra 는 간단한 Ruby 웹 애플리케이션을 빠르고 쉽게 작성할 수 있게 도와주는 경량 웹 프레임워크 입니다.
(출시 버전 정보 : https://rubygems.org/gems/sinatra/versions/)
EXPOSE 8080 : 컨테이너의 8080 포트를 오픈합니다.
CMD ["rackup", "-o", "0.0.0.0", "-p", "8080"] : 컨테이너가 시작될 때 /app 디렉토리에서 config.ru 를 찾아 Rack 애플리케이션을 실행하고,
호스트의 8080 포트와 컨테이너 내부의 8080 포트를 연결합니다.
빌드 실행하면 새로운 도커 이미지가 생성됩니다.
# docker build -t ruby-app .
# docker images |grep ruby-app
ruby-app latest 24f660cbf71f 5 minutes ago 915 MB
로컬의 도커 이미지를 Registry 에 올려놓고 사용하도록 합니다.
# docker tag ruby-app default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/ruby
# docker push default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/ruby
ruby 응용프로그램을 배포하기 위해 yaml 파일을 작성합니다.
# vi app.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: ruby-sample spec: replicas: 1 selector: matchLabels: app: ruby-sample template: metadata: labels: app: ruby-sample spec: containers: - name: ruby-sample image: default-route-openshift-image-registry.apps.az1.sysdocu.kr:5000/project412/ruby ports: - containerPort: 8080 protocol: TCP imagePullSecrets: - name: sysdocu --- apiVersion: v1 kind: Service metadata: name: ruby-sample spec: type: LoadBalancer selector: app: ruby-sample ports: - name: ruby-sample protocol: TCP port: 8080 targetPort: 8080 |
* 참고
imagePullSecrets 에 sysdocu 는 registry 접근 권한을 가진 사용자 입니다.
secret 생성 방법은 다른 포스트를 참고해주세요. ( https://sysdocu.tistory.com/1776 )
위 yaml 파일을 적용면 deployment, pod, service, endpoint 가 모두 생성됩니다.
# oc apply -f app.yaml
deployment.apps/ruby-sample created
service/ruby-sample created
추가로 외부에서 접근이 가능하도록 route 를 생성합니다.
# oc expose service ruby-sample --name=ruby-sample --port=8080
route.route.openshift.io/ruby-sample exposed
생성된 route 정보를 확인합니다.
# oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
go-sample go-sample-project412.apps.az1.sysdocu.kr go-sample 8888 None
java-sample java-sample-project412.apps.az1.sysdocu.kr java-sample 8080 None
nodejs-sample nodejs-sample-project412.apps.az1.sysdocu.kr nodejs-sample 8080 None
python-sample python-sample-project412.apps.az1.sysdocu.kr python-sample 8080 None
ruby-sample ruby-sample-project412.apps.az1.sysdocu.kr ruby-sample 8080 None
외부에서 호스트명을 이용해 컨테이너 접근이 가능합니다.
curl 명령으로 접근하면 html 코드가 많이 출력되기 때문에 브라우저로 완성된 페이지를 보는것이 좋습니다.
- 웹브라우저 접속 URL : ruby-sample-project412.apps.az1.sysdocu.kr
'리눅스 > OpenShift' 카테고리의 다른 글
Openshift registry 내부 레지스트리에 로그인되지 않는 경우 (0) | 2023.03.20 |
---|---|
oc patch configs.imageregistry.operator.openshift.io 명령으로 삭제한 이미지 레지스트리 복구하기 (0) | 2023.03.17 |
Openshift 4.12.0 응용프로그램 배포 - 웹서버, 데이터베이스 (0) | 2023.02.17 |
Openshift 4.12.0 내부 이미지 레지스트리 (Private Image Registry) 구성 (0) | 2023.02.16 |
Openshift 4.12.0 응용프로그램 관리 (Pod, Deployment, Service), 스케일아웃 (Replica), 로드발란서 (LoadBalancer) 설정 (0) | 2023.02.15 |