애플리케이션을 배포하다 보면 환경에 따라서 다른 설정값을 사용하는 경우가 있다.
예를 들어 database의 IP, API를 호출하기 위한 API KEY, 개발/운영에 따른 디버그 모드, 환경 설정 파일들이 있다.
애플리케이션 이미지는 같지만, 이런 환경 변수가 차이가 나는 경우 매번 다른 컨테이너 이미지를 만드는 것은 관리상 불편할 수 밖에 없다.
이러한 환경 변수나 설정값들을 변수로 관리해서 Pod가 생성될때 이 값을 넣어줄 수 있는데, 이러한 기능을 제공하는 것이 바로 Configmap과 Secret이다.
아래 그림과 같이 설정 파일을 만들어놓고, Pod 를 배포할때 마다 다른 설정 정보를 반영하도록 할 수 있다.
Configmap이나 Secret에 정의해놓고 이 정의해놓은 값을 Pod로 넘기는 방법은 크게 두가지가 있다.
- 정의해놓은 값을 Pod의 환경 변수 (Environment variable)로 넘기는 방법
- 정의해놓은 값을 Pod의 디스크 볼륨으로 마운트 하는 방법
ConfigMap
configmap은 앞서 설명한것과 같이 설정 정보를 저장해놓는 일종의 저장소 역할을 한다.
configmap은 키/밸류 형식으로 저장이된다. configmap을 생성하는 방법은 literal (문자)로 생성하는 방법과 파일로 생성하는 방법 두가지가 있다. 이 두가지 방법에 대해 알아보자.
[1] Literal
먼저 간단하게 문자로 생성하는 방법을 알아보자.
키를 “language”로 하고 그 값이 “java”인 configMap을 생성해보자
Kubectl create configmap [configmap 이름] –from-literal=[키]=[값]
식으로 생성하면 된다.
아래 명령을 이용하면, hello-cm 이라는 이름의 configMap에 키는 language, 값은 java인 configMap이 생성된다.
% kubectl create configmap hello-cm --from-literal=language=java
또는 아래와 같이 YAML파일로도 configMap을 생성할 수 있다.
hello-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: hello-cm
data:
language: java
데이타 항목에 [키]:[값] 형식으로 라인을 추가하면 여러 개의 값을 하나의 configMap에 저장할 수 있다.
configmap이 생성되었으면 이 값을 Pod에서 환경 변수로 불러서 사용해보도록 하자.
node.js로 간단한 웹 애플리케이션을 만든후에 “LANGUAGE”라는 환경 변수의 값을 읽어서 출력하도록 할것이다.
아래와 같이 server.js node.js 애플리케이션을 만든다.
var os = require('os');
var http = require('http');
var handleRequest = function(request, response) {
response.writeHead(200);
response.end(" my prefered language is "+process.env.LANGUAGE+ "\n");
//log
console.log("["+
Date(Date.now()).toLocaleString()+
"] "+os.hostname());
}
var www = http.createServer(handleRequest);
www.listen(8080);
이 파일을 컨테이너로 패키징한 후에 아래와 같이 Deployment를 정의한다.
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: cm-deployment
spec:
replicas: 3
minReadySeconds: 5
selector:
matchLabels:
app: cm-literal
template:
metadata:
name: cm-literal-pod
labels:
app: cm-literal
spec:
containers:
- name: cm
image: gcr.io/terrycho-sandbox/cm:v1
imagePullPolicy: Always
ports:
- containerPort: 8080
env:
- name: LANGUAGE
valueFrom:
configMapKeyRef:
name: hello-cm
key: language
configMap에서 데이타를 읽는 부분은 맨 아래에 env 부분인데, env 부분에 환경 변수를 정의한다.
name은 LANGUAGE라는 이름으로 정의하고 데이타는 valueFrom을 이용해서 configMap에서 읽어오도록 하였다.
name에는 configMap의 이름인 hello-cm을, 그리고 읽어오고자 하는 데이타는 키 값이 “language”인 값을 읽어오도록 하였다.
이렇게 하면 LANGUAGE 환경 변수에, configMap에 “language” 로 저장된 “java”라는 문자열을 읽어오게 된다.
이 스크립트를 이용하여 Deployment를 생성한 후에, 이 Deployment 앞에 Service (Load balancer)를 붙여 보자.
apiVersion: v1
kind: Service
metadata:
name: cm-literal-svc
spec:
selector:
app: cm-literal
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
type: LoadBalancer
서비스가 생성이 되었으면 웹 브라우져에서 해당 Service의 URL을 접속해보자.
위와 같이 환경 변수에서 “java”라는 문자열을 읽어와서 출력한것을 확인할 수 있다.
[2] File
위와 같이 개개별 값을 공유할 수 도 있지만, 설정을 파일 형태로 해서 Pod에 공유하는 방법도 있다.
예제를 보면서 이해하도록 하자.
profile.properties라는 파일이 있고 파일 내용이 아래와 같다고 하자.
myname=terry
email=myemail@mycompany.com
address=seoul
파일을 이용해서 ConfigMap을 만들때는 아래와 같이 –from-file 을 이용해서 파일명을 넘겨주면 된다.
%kubectl create configmap cm-file --from file=./properties/profile.properties
이렇게 파일을 이용해서 configMap을 생성하면, 아래와 같이 키는 파일명이 되고, 값은 파일 내용이 된다.
[1] 환경변수로 값 전달하기
생성된 configMap 내의 값을 Pod로 전달하는 방법은 앞에서 예를든 것과 같이 환경 변수로 넘길 수 있다.
아래 Deployment 예제를 보자.
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: cm-file-deployment
spec:
replicas: 3
minReadySeconds: 5
selector:
matchLabels:
app: cm-file
template:
metadata:
name: cm-file-pod
labels:
app: cm-file
spec:
containers:
- name: cm-file
image: gcr.io/terrycho-sandbox/cm-file:v1
imagePullPolicy: Always
ports:
- containerPort: 8080
env:
- name: PROFILE
valueFrom:
configMapKeyRef:
name: cm-file
key: profile.properties
cm-file configMap에서 키가 “profile.properties” (파일명)인 값을 읽어와서 환경 변수 PROFILE에 저장한다. 저장된 값은 파일의 내용인 아래 문자열이 된다.
myname=terry
email=myemail@mycompany.com
address=seoul
혼동하지 말아야 하는 점은 profile.properties 파일안에 문자열이 myname=terry 처럼 키/밸류 형식으로 되어 있다고 하더라도, myname 을 키로 해서 terry라는 값을 가지고 오는 것처럼 개개별 문자열을 키/밸류로 인식하는 것이 아니라 전체 파일 내용을 하나의 문자열로 처리한다는 점이다.
[2] 디스크 볼륨으로 마운트하기
configMap의 정보를 pod로 전달하는 방법은 앞에 처럼 환경 변수를 사용하는 방법도 있지만, Pod의 디스크 볼륨으로 마운트 시키는 방법도 있다.
앞의 cm-file configMap을 /tmp/config/에 마운트 해보도록 하자.
아래와 같이 Deployment 스크립트를 작성한다.
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: cm-file-deployment-vol
spec:
replicas: 3
minReadySeconds: 5
selector:
matchLabels:
app: cm-file-vol
template:
metadata:
name: cm-file-vol-pod
labels:
app: cm-file-vol
spec:
containers:
- name: cm-file-vol
image: gcr.io/terrycho-sandbox/cm-file-volume:v1
imagePullPolicy: Always
ports:
- containerPort: 8080
volumeMounts:
- name: config-profile
mountPath: /tmp/config
volumes:
- name: config-profile
configMap:
name: cm-file
configMap을 디스크 볼륨으로 마운트해서 사용하는 방법은 volumes 을 configMap으로 정의하면 된다.
위의 예제에서 처럼 volume을 정의할때, configMap으로 정의하고 configMap의 이름을 cm-file로 정의하여, cm-file configMap을 선택하였다. 이 볼륨을 volumeMounts를 이용해서 /tmp/config에 마운트 되도록 하였다.
이때 중요한점은 마운트 포인트에 마운트 될때, 파일명이 configMap내의 키가 파일명이 된다.
다음 테스트를 위해서 server.js 애플리케이션에 /tmp/config/profile.properties 파일을 읽어서 출력하도록 아래와 같이 코드를 작성한다.
var os = require('os');
var fs = require('fs');
var http = require('http');
var handleRequest = function(request, response) {
fs.readFile('/tmp/config/profile.properties',function(err,data){
response.writeHead(200);
response.end("Read configMap from file "+data+" \n");
});
//log
console.log("["+
Date(Date.now()).toLocaleString()+
"] "+os.hostname());
}
var www = http.createServer(handleRequest);
www.listen(8080);
이 server.js를 도커로 패키징해서 배포한후, service를 붙여서 테스트해보면 다음과 같은 결과를 얻을 수 있다.
파일 내용이 출력되는 것을 확인할 수 있다.
디스크에 마운트가 제대로 되었는지를 확인하기 위해서 Pod에 쉘로 로그인해서 확인해보자.
그림과 같이 /tmp/config/profile.properties 파일이 생성된 것을 확인할 수 있다.
이번 글에서는 환경에 따라서 설정값을 사용하기 위한 방법 중에서 ConfigMap에 대해 알아보았다.
다음 포스트는 Secret에 대해 알아보자!