Docker를 이용한 서비스 컨테이너화 작업 - 2

최민길(Gale)·2023년 5월 14일
1

Docker & Container

목록 보기
2/4

안녕하세요 오늘은 저번 포스팅에 이어서 컨테이너 내의 프로젝트 파일들을 실행시키고 redis 컨테이너를 만들어 이와 연동시키는 작업을 진행해보도록 하겠습니다.

우선 DB 엔드포인트 및 로그인 정보, JWT 암호 키 등 보안이 요구되는 항목들을 최대한 코드에서 작성하지 않고 환경 변수로 입력받아 빌드 시 해당 값을 참조하는 식으로 수정했습니다. Dockerfile에는 ARG와 ENV 명령어가 존재합니다. ARG의 경우 정의된 순간부터 빌드되는 시점까지 사용되며 컨테이너 실행 시에는 ARG 변수에 접근할 수 없습니다. 반면 ENV의 경우 컨테이너를 실행 시 덮어쓰기가 가능합니다. 저는 ARG로 이미지 빌드 시 환경 변수를 생성하여 이를 ENV로 넘겨 주는 방식으로 진행하여 사용했습니다.

ARG 	
	jwt_secret_key \
	server_url \
    ...

ENV 	
	JWT_SECRET_KEY=$jwt_secret_key \
	SERVER_URL=$server_url \
    ...

위의 환경 변수값을 PHP 내에서 참조할 수 있는 env.php 파일을 생성합니다. 파일 내에서 각 변수를 const로 설정하여 값이 변경되지 않게 되는 안정성과 메모리 측면에서의 성능 향상 등을 고려했습니다.

// 로그인 키값
const JWT_SECRET_KEY = 'JWT_SECRET_KEY_SAMPLE';

const SERVER_URL = 'SERVER_URL_SAMPLE';

...

이어서 env.php 내의 값들을 환경 변수로 대체하는 작업을 진행합니다. 저번 시간에 사용했던 sed -i 명령어를 이용하여 진행합니다. 이 때 RUN에서 환경 변수를 참조할 때는 $ENV의 형식으로 참조하지만 WORKDIR에서 참조 시에는 ${ENV}로 참조합니다.

WORKDIR /var/www/${BRANCH}
RUN	sed -i 's%JWT_SECRET_KEY_SAMPLE%'$JWT_SECRET_KEY'%g' env.php && \
	sed -i 's%SERVER_URL_SAMPLE%'$SERVER_URL'%g' env.php && \
    ...

PHP 프로젝트를 클론 후 정상적으로 실행하기 위해선 권한 허용이 되어야 합니다. 현재 프로젝트의 경우 log를 자동적으로 생성하는 작업을 진행하는데 이를 위해 읽기 실행 권한 뿐만 아니라 쓰기 권한도 같이 부여하면 Dockerfile 수정은 마무리됩니다.

수정된 Dockerfile로 이미지 빌드 시 한 가지 주의해야 할 점이 있습니다. 저는 Dockerfile 내에 프로젝트를 clone하는 방식으로 최신화를 진행하고 있는데, 빌드 시 --no-cache 옵션이 없다면 캐싱된 이전 데이터 값으로 이미지가 빌드되어 변경 사항이 푸시되어도 컨테이너 내부의 파일에는 반영되지 않는 현상이 발생합니다. 따라서 이미지 빌드 시 --no-cache 옵션을 꼭 사용해주시면 좋습니다.

Redis의 경우 Redis 이미지를 활용하여 컨테이너를 빌드하였습니다. 기본적으로 컨테이너의 격리성 때문에 다른 컨테이너와 네트워크 통신을 진행할 수 없습니다. 이 때 두 컨테이너 간 네트워크 연결을 위해 network를 생성합니다. docker network create 명령어를 사용하면 Docker 네트워크를 생성할 수 있습니다.

네트워크 타입은 크게 bridge, host, overlay 방식 등이 존재합니다. bridge 네트워크의 경우 하나의 호스트 컴퓨터 내에서 여러 컨테이너들이 통신이 가능한 타입입니다. host 네트워크는 호스트 컴퓨터와 동일한 네트워크 환경에 컨테이너를 통신하는 타입입니다. overlay 네트워크는 여러 호스트에 분산되어 돌아가는 컨테이너를 통신하는 타입입니다. 현재 저희 서비스의 경우 하나의 서버 내에서 여러 속성들이 유기적으로 맞물려있는 형태이기 때문에 하나의 호스트 서버 내에서 서로 통신이 가능한 bridge 타입을 선택했습니다.

Docker 네트워크를 생성하면 컨테이너를 실행 시 --network [네트워크 이름] 옵션을 추가하여 진행하면 해당 네트워크에 컨테이너가 종속되게 됩니다. 따라서 해당 네트워크에 존재하는 컨테이너끼리는 내부에서 할당받은 IP 주소를 통해 통신이 가능합니다. 이 때 각 컨테이너 별 할당받은 IP 주소는 docker network inspect [네트워크 이름] 명령어를 이용하여 확인할 수 있습니다. 다음은 해당 명령어를 실행한 결과입니다. 보시는 것처럼 toda라는 네트워크 안에 toda_test, redis의 두 컨테이너가 존재합니다. bridge 타입으로 되어 있어 내부의 컨테이너끼리 각각 할당받은 172.19.0.2, 172.19.0.3으로 통신이 가능합니다.

[
    {
        "Name": "toda",
        "Id": "{ID}",
        "Created": "2023-05-14T04:03:55.275208216Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "{ID}": {
                "Name": "toda_test",
                "EndpointID": "{ID}",
                "MacAddress": "{ADDRESS}",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            },
            "{ID}": {
                "Name": "redis",
                "EndpointID": "{ID}",
                "MacAddress": "{ADDRESS}",
                "IPv4Address": "172.19.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

여기서 한 가지 의문이 드실 수 있습니다. 컨테이너는 격리성을 띄는 일종의 가상화 툴인데 RDS를 사용할 경우 보안 그룹에 생성한 네트워크의 주소를 추가해주어야 정상적으로 DB에 접근이 가능한 지 궁금하실 수 있습니다. 결론부터 말씀드리자면 그렇게 하지 않아도 됩니다. 컨테이너는 OS level은 호스트 pc와 공유하기 때문에 생성된 컨테이너를 외부에서 접근 시 호스트 pc의 IP 주소로 접근하시면 됩니다. 단 주의할 점은 컨테이너 실행 시 -p 옵션을 이용하여 호스트 pc에서 사용할 포트를 포트포워딩할 수 있으므로 외부에서 접근 시 몇 번 포트로 접근 가능하게 할 것인지를 같이 설정하여 진행하시면 되겠습니다. 그럼 이상으로 오늘의 포스팅 마치도록 하겠습니다!

profile
저는 상황에 맞는 최적의 솔루션을 깊고 정확한 개념의 이해를 통한 다양한 방식으로 해결해오면서 지난 3년 동안 신규 서비스를 20만 회원 서비스로 성장시킨 Software Developer 최민길입니다.

0개의 댓글