개발자의 오르막
golang과 Docker로 환경변수 제어하기 본문
환경변수란?
환경변수는 프로세스가 컴퓨터에서 동작하는 방식에 영향을 미치는 동적인 값들의 모임이다.
우리가 흔히 Java 와 golang 을 처음 설치할 때 설정해주는 JAVA_HOME, GO_PATH 처럼 서버에서 특정 값을 담아, 해당 변수만 호출하여 다른 프로세스도 그 값을 참조하게끔 사용하는 변수이다.
그렇다면 위의 환경변수는 우리가 개발할 때 어떤 용도로 많이 쓰일 수 있을까?
- 하나의 서버에서 여러 프로세스가 공통적으로 사용하는 정보
- ex) DB 접속 정보, 언어 환경변수, 주키퍼와 같은 접속서버
- 하나의 프로세스가 여러 서버에 배포되는 경우
- DEV, STAGE, PROD 등 같은 프로세스이지만 다른 서버에 배포될 때
우리는 제일 보편적으로 config 파일에서 사용하는 정보를 환경변수로 전달할 수 있으며, 이를 Docker나 쿠버네티스를 통해 각 서버마다 다른 환경변수로 손 쉽게 정보를 전달할 수 있다.
이렇게 사용하면 접속 정보가 변경된다 하더라도, 우리는 일일이 config 파일을 업데이트 하여 올리지 않을 수 있다.
그럼 먼저 어플리케이션에서 환경변수를 어떻게 로직으로 담을 수 있는지 알아보자.
Golang 의 Envconfig
- envconfig 는 환경 변수에서 구성을 분석하고 임의의 구조체를 채울 수 있는 라이브러리
- 지원 유형
- time.Duration 은 기본 지원
- slices 및 arrays
- struct
- Unmarshaler 인터페이스를 통한 사용자 정의 유형
- config.cfg 파일을 파싱해서 변수로 사용하는 것보다 환경변수를 활용하여 변수로 활용하게 되면 Docker 나 Kube 에서 환경변수로 지정한 값으로 자유롭게 활용 가능하다. (config.cfg 파일 변경 소요가 없어짐)
Basic Code
package main
import (
"fmt"
"log"
"github.com/kelseyhightower/envconfig"
)
type Config struct {
Foo string `default:"bar"`
}
func main() {
var cfg Config
if err := envconfig.Process("app", &cfg); err != nil {
log.Fatalln(err)
}
fmt.Println(cfg.Foo)
}
- Config 구조체에 입력한 필드에 대한 default 값은 envconfig.Process("app", &cfg) 를 통해 값이 주입된다.
- 이 때 주의할 점은 구조체의 필드는 대문자로 해야 사용할 수 있다.
- golang 에서는 환경변수를 담을 수 있는 구조체를 선언하고, 해당 환경변수의 값이 존재하면 그 값을 주입하며, 없을 때는 default 에 명시된 값을 주입한다.
위처럼 어플리케이션에서 환경변수가 존재하는 경우 그 값을 구조체에 담아 사용하는 것을 알 수 있었다. 그럼 이러한 환경변수는 어디서 설정하여 어떻게 전달하는 걸까?
물론 실제 서버에 들어가서 export {환경변수명}={환경변수값} 의 cmd 로 직접 지정하는 방법도 있다. 하지만 우리 개발자가 일일이 모든 서버에 들어가서 환경변수 값을 수정할 것은 아니기 때문에 다른 방법이 있어야 한다.
Docker 로 환경변수 값 전달하기
도커는 서비스를 컨테이너화 시켜 하나의 서버 안에서 독립적인 환경을 만들어주는 서비스이다. 기본적으로는 각 컨테이너끼리 다른 컨테이너의 정보를 모르기 때문에, Docker 를 구동하는 호스트에서 해당 정보를 알려줘야 한다. 이 때 우리는 환경변수를 사용한다.
환경변수 지정 방법
- Dockerfile 로 환경변수 지정
- Docker run 명령어를 실행 시 환경변수 지정
Dockerfile 로 환경변수 지정
ENV LANG ko_KR.UTF-8
ENV LANGUAGE ko_KR.UTF-8
ENV LC_ALL ko_KR.UTF-8
환경변수를 ENV {환경변수 이름} {환경변수 값} 의 형태로 도커파일에서 지정하면 도커 이미지를 빌드할 때 위의 환경변수에 값이 주입되어 사용할 수 있게된다.
Docker run 명령어를 통한 환경변수 사용
도커 이미지를 이용해 컨테이너를 만들 때마다 환경변수를 변경하고 싶을 때가 있다. 그럴 때 사용하는 것이 docker run 수행시 환경변수를 지정하는 것이다.
sudo docker run -e HOME=/home/complusblog/workspace/helloword helloworld
위처럼 docker run -e {환경변수이름}={환경변수값} 의 형태로 사용하면 환경변수에 값이 주입된다.
Docker 환경변수 적용 여부에 대한 확인 방법
- 환경변수가 잘 적용되었는지는 docker inspect {컨테이너ID} 명령어를 통해 알 수 있다.
- 다른 방법으로 docker exec {컨테이너ID} env 를 통해서 알 수 있다.
- docker exec -it {컨테이너ID} bin/bash 로 해당 컨테이너에 들어가 export 명령어를 선언하면 적용된 환경변수 목록을 알 수 있다.
그러나 우리는 환경변수가 너무 많아지면 아래와 같은 명령어를 사용해야 할 수도 있다.
sudo docker run -d -t -i -e REDIS_NAMESPACE='staging' \
-e POSTGRES_ENV_POSTGRES_PASSWORD='foo' \
-e POSTGRES_ENV_POSTGRES_USER='bar' \
-e POSTGRES_ENV_DB_NAME='mysite_staging' \
-e POSTGRES_PORT_5432_TCP_ADDR='docker-db-1.hidden.us-east-1.rds.amazonaws.com' \
-e SITE_URL='staging.mysite.com' \
-p 80:80 \
--link redis:redis \
--name container_name dockerhub_id/image_name
예를들면 DEV, STAGE, PROD 에는 각기 다른 환경변수를 전달해야 하는데, 그럼 각 서버별로 환경변수를 다르게 한 명령어를 사용해야 하는 단점이 생길 수 있다. 뿐만 아니라 이는 배포할 때 개발자가 명령어를 잘못치면 오류상황이 야기될 수도 있다.
Docker Compose 에서 각 서비스 컨테이너에 환경변수 전달
Docker Compose 란 복수 개의 컨테이너를 실행시키는 도커 어플리케이션이 정의를 하기 위한 툴입니다.
Compose 를 사용하면 YAML 파일을 사용하여 애플리케이션의 서비스를 구성할 수 있습니다.
docker Compose 를 이용하면 각 컨테이너 별 같은 환경변수에 서로 다른 값을 넣는 것을 정의할 수 있다.
- docker-compose.yml
version: '3.1'
services:
zoo1:
image: zookeeper
restart: always
hostname: zoo1
ports:
- 2181:2181
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=0.0.0.0:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181
zoo2:
image: zookeeper
restart: always
hostname: zoo2
ports:
- 2182:2181
environment:
ZOO_MY_ID: 2
ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=0.0.0.0:2888:3888;2181 server.3=zoo3:2888:3888;2181
- ZOO_MY_ID , ZOO_SERVERS 는 같은 환경변수임에 불구하고, docker-compose.yml 에서 서로 다른 값을 주입하여 활용될 수 있음을 보여준다.
version: '3.5'
services:
web:
build:
context: .
dockerfile: Dockerfile-dev
특히 docker compose 는 각 서비스 별 참조하는 도커파일을 지정할 수 있기 때문에 용도별, 서버별 Dockerfile 을 따로 생성하여 해당 컨테이너를 실행시킬 수 있습니다.
환경변수 값이 선택되는 우선순위
- Compose 파일에 직접 입력한 값
- 쉘 환경변수로 등록한 값
- 환경변수 파일로 입력된 값 ( .env 등 )
- Dockerfile 을 통해 삽입된 값
Reference
- https://github.com/kelseyhightower/envconfig
- https://pkg.go.dev/github.com/vrischmann/envconfig
- https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=complusblog&logNo=220975099502
- https://seongjin.me/environment-variables-in-docker-compose/
- https://docs.docker.com/compose/environment-variables/#set-environment-variables-with-docker-compose-run
'GoLang' 카테고리의 다른 글
Go, Makefile, Docker로 프로젝트 셋팅 및 빌드 정보 활용하기 (0) | 2022.10.08 |
---|---|
Golang 과 TDD (0) | 2022.10.02 |
GoLang 의 포인터 (0) | 2022.07.03 |
redis: discarding bad PubSub connection: redis: ping timeout 이슈 (0) | 2022.06.17 |
고루틴과 동시성 프로그래밍 (0) | 2022.05.18 |