MongoDB 의 선택과 마이그레이션

이 글은 지난 2주간 MySQL 에 존재했던 레코드 수가 5억건이 넘는 주요 관계 테이블을 NoSQL 로 마이그레이션하는 과정에서 겪었던 내용을 근거로 얻게된 경험과 인사이트를 공유하기 위해서 작성합니다.

NoSQL Database 의 선택

RDB 에 꾸준하게 쌓아왔던 관계 자료의 데이터량 증가로 인하여, 전체적인 쿼리 속도가 느려지고, 데이터베이스의 부하가 높아지는 것이 감지되어, 파티셔닝을 할지, 아니면 NoSQL 을 도입할지를 고민하다가, 현재 사용중인 인프라가 AWS 이었기에 AWS DynamoDB 로의 이전을 우선적으로 고려하였습니다. 하지만, NoSQL 의 경우, 종류도 다양하고 사용법도 많이 다르기 때문에, 해당 데이터베이스가 사용하려는 use case 에 가장 적합한 솔루션인지를 조사하기 시작했습니다. 유튜브에서 Rick Houlihan 의 AWS DynamoDB 400대 세션들을 들어보면 다양한 use-case 별로 best practice 를 설명하고 있기에, 해당 내용들을 들어보면 구현을 위한 큰 그림을 정립해 나가는데 많은 도움을 얻을 수 있습니다. 개인적으로는, 거의 약장수처럼 스무스하게 발표하는 그 아저씨의 능력, 참으로 부럽더군요. 어쨌거나, 그의 설명으로부터 얻을 수 있었던 인사이트는, NoSQL 의 문제점
  • 모든 NoSQL 은 horizontal 스케일링을 위해서, 여러개의 노드에 걸친 sharding 을 자동으로 하는 기능이 있는데, 그 해쉬 키가 evenly distributed 되지 않으면, 일부 sharding 으로만 hot 한 데이터가 몰리게 되고, 그런식으로 일부 노드에 주요 데이터가 집중되는 경우, 동일한 성능의 여러개의 노드 클러스터로 구성된 NoSQL DB 가 분산처리를 최적으로 수행하지 못해서, 결국 낮은 성능의 노드 인스턴스의 한계로 인한 비효율적으로 cluster 전체가 버벅거리고 Exception 을 쏟아내는 문제가 일어납니다.
  • 위의 문제와는 별개로, 일정한 term 동안 발생하는 요청의 수도, 위의 경우와 비슷하게 핫한 요청이 특정 노드에 집중될 수 있는데, 이 경우도 비슷한 문제가 발생합니다. 어떤 노드에 있는 자료에 대해서 집중적으로 쓰기/읽기 요청의 갯수가 순식 간에 올라가는 경우도 위의 경우처럼 결국 Exception 이 발생할 것이기 때문입니다.
  • 따라서, 아래 canonical use cases 를 정리한 것처럼, NoSQL 의 효율을 극대화 시키기 위해서는, 일정 클러스터 안에서 분산된 노드들을 가지고 운영하는 분산 DB 구조상, NoSQL 모든 노드의 data 를 쓰는 요청의 횟수와, data 를 읽는 요청의 횟수가, 시간대 별로 거의 유사한 패턴으로 읽어질 수 있도록 데이터를 잘 분산시켜놓고 운용하는 것입니다. (하지만, 이렇게 이론적으로 이상적인 구조에 비슷한 데이터를 갖는 설계가 현실적으로 어려운 것이 맹점.)
    • doing key-value lookups on well-distributed records
    • avoiding complex queries
    • most importantly, limiting hot keys
암튼, 이런 문제점은 best practice 를 따라하면서 최소화 하는 방향으로 진행하면 되겠지 싶었지만, AWS 에서 제공하는 레퍼런스 내용에 나오는 DynamoDB 의 구현방법이나 best practice 말고, 실제 DynamoDB 를 장기적으로 경험해본 사람들의 핸즈온 경험들이 궁금해 졌습니다. 좀더 구글링을 해보니, 아래에 리스팅한 글들에서처럼 생각보다 부정적인 경험자들의 의견들이 있었습니다. 종합해보면, DynamoDB 의 가장 큰 문제점들 중 하나는 throttle exception 발생 경우가 많아져서, throughput (RCU/WCU) 을 올리다 보면, 실제로 운용에 필요한 비용이 처음 생각했던 것 보다 많이 드는 문제점에 대한 지적입니다. 실제로 reddit 에서 Aurora DB 팀이, nosql 이라고 말하긴 했지만, DynamoDB 운용비보다 40% 정도 낮은 장점이 있다는 아래 글에 대한 언급이 있었는데, 고비용의 문제가 상당히 걸림돌로 작용하는 것으로 보입니다.
“The one thing that surprised me is that there are some customers who are moving their nosql workload to aurora. There are two reasons for that. One, it’s a lot easier to use Aurora because it’s mysql compatible compared to nosql because the interfaces and transaction characteristics are so different. What is also interesting is people also saved money because the IO cost is much lower. In no SQL if you have a large table it gets partitioned then the IO gets partitioned across all the table partitions that you have. And if you have one partition that is hot then you have to provision based on the IO requirement of the hot partition. In the case of Aurora we do automatic heat management so we don’t have this hot parition issue. Second we don’t charge based on provisioned IO. It’s only the IO that you use. And that actually saves a lot of money. In this particular case this is a big social company, interaction company, I cannot tell the name, and they reduced their operational costs by 40% by moving from nosql to Aurora”
DynamoDB 의 장점은 그냥 사용하면 되는 managed 서비스라지만, 이것 때문에 상당히 strict 한 throughput exception 을 감수하고, 익셉션이 잡힐때마다 큐를 써서 재시도를 하도록 만들던지, 돈을 많이 써서 널널하게 고사양으로 운용해야 한다고 생각하니, 별로 좋은 그림처럼 보이지 않았습니다. 그래서, 알아본 다른 대안이 MongoDB 입니다. 우선, NoSQL 중 가장 많은 사용자에게 선택받은 데이터베이스이면서, 최소한 앞서 언급한 DynamoDB 와 같은 strict 한 throughput exception 이 없기때문에 클러스트 업그레이드 시점을 그만큼 더 유연하게 가져갈 수 있을 겁니다. 하지만, AWS 가 제공하는 매니지드 서비스가 없어서 운용적으로 시간과 노력이 더 들어가지 않나 싶은 걱정때문에, 개인적인 선호도 우선 순위에서 DynamoDB 보다 낮았습니다. 그런데 AWS 서울 리젼에서도 이용할 수 있는 몽고DB 매니지드 서비스인 MongoDB Atlas 가 있더군요. TL;DR
DynamoDB MongoDB
a fully managed proprietary NoSQL open source NoSQL
1개 document 최대 size = 400KB 1개 document 최대 size = 16 MB
aggregation 미지원 (DynamoStream 과 Lambda 로 처리 또는 application level 에서 처리) 훌륭한 appregation framework 지원
예) SELECT COUNT GROUP BY 구현 (recursive 하게 여러 RCU 소비하면서 복잡하게 처리) 예) SELECT COUNT GROUP BY (간단한 처리 가능)
로컬용 stripped version 만 제공 로컬에 Docker 등으로 원하는 버젼 설치 가능
디비접속 클라이언트 애매 디비접속 클라이언트 MongoDB Compasss / Studio 3T 등 다양하게 존재
위와 같은 장점들을 나열해 보니 MongoDB 로의 전환이 당연하게 느껴졌습니다.

AWS RDS MySQL 에서 MongoDB 로의 마이그레이션

사용할 NoSQL 을 결정했기때문에, 5억개가 넘는 MySQL 의 레코드를 MongoDB 로 마이그레이션하기로 했습니다. 처음에는 마이그레이션을 수행할 application 프로그램을 작성해서 수행하도록 해봤지만, 레코드의 갯수가 워낙 많아서, 마이그레이션만 수일이 넘는 시간이 걸리게 됨을 알게 되었습니다. 그래서, mongoimport 를 사용하기로 결정을 바꾸고, 이를 위해서 우선적으로 MySQL 의 필요한 데이터들을 CSV 파일로 덤프하기로 했습니다. AWS RDS MySQL 의 경우, CSV 덤프를 할 수 없었기에, 스냅샷을 생성한 후 Migrate snapshot 액션 메뉴를 통해서 AuroraDB 로 마이그레이션을 수행한 다음, AuroraDB 로부터 CSV 파일을 생성하기로 했습니다. RDS 테이블 스키마가, NoSQL 의 테이블과 비슷한 형태라면, 아래와 같이 mysqldump 로 dump 파일을 생성한 후, CSV 변환을 생각해 볼 수 있겠으나, 조인 쿼리도 없이 단순하게 RDB 테이블을 NoSQL 테이블로 옮길 가능성은 거의 존재하지 않을 것이므로, AuroraDB 를 사용할 수 밖에 없을 듯 합니다.
mysqldump -h aurora.co8ookxfhjax.ap-northeast-2.rds.amazonaws.com -u admin -p --single-transaction --compress --routines --triggers database_name table_name > table_dump.sql
AuroraDB 에서 CSV 파일을 생성하는 방법은 아래의 레퍼런스에 잘 설명이 되어있습니다. Saving Data from an Amazon Aurora MySQL DB Cluster into Text Files in an Amazon S3 Bucket 위 메뉴얼에 따라, 아래 순서대로 작업을 수행했습니다.
  1. AWS S3 버킷 생성
  2. AWS RDB 스냅샷을 선택후 instance actions 에서 Aurora DB 로 migration 을 선택 (db.r4.xlarge)
  3. IAM 메뉴에서 Aurora 클러스터의 IAM 파라미터 변경
  4. Aurora DB 에 접속후, 아래 명령수행
GRANT SELECT INTO S3 ON . TO 'admin'@'%';
FLUSH PRIVILEGES;
USE database_name;
SELECT table_name.*, IFNULL(another_table_name.type, "Rating") AS rating_type, users.gender, users.created_at AS registered_at, ... FROM table_name LEFT JOIN users ON table_name.user_id = users.id LEFT JOIN another_table_name ON table_name.id = another_table_name.table_id INTO OUTFILE S3 's3-ap-northeast-2://my-data/sample_data' FIELDS TERMINATED BY ',' LINES TERMINATED BY 'n' OVERWRITE ON
AuroraDB 클러스터에 접속후 위의 명령을 내리니까, 약 3시간후 S3에 CSV 파일들이 6GB 단위 chunk 들 10개가 생성되어 있는 것을 확인 할 수 있었습니다.

AWS 에서 CloudFormation 으로 MongoDB 클러스터 생성하기

아래의 링크에서 볼 수 있듯, AWS 는 MongoDB 클러스터를 생성하기위한 CloudFormation 템플릿을 제공하므로, 우선적으로 아래 문서를 참고하여 MongoDB 클러스트를 설치했습니다. MongoDB on the AWS Cloud: Quick Start Reference Deployment 위 템플릿은 크게 2가지의 옵션을 지원하는데, 1번째 템플릿 옵션을 선택하면, MongoDB 클러스트를 위하여 새로운 VPC 를 생성하고 그 VPC 상에 MongoDB 클러스터를 생성해줍니다. 그리고, 해당 클러스터에 접속가능한 bastion 서버까지 자동으로 생성해줍니다. 그러므로, 해당 bastion 서버에 application 프로그램을 배포해야 합니다. 각자 선택은 자유겠지만, 제가 작업한 환경에서는 이미 VPC 에 설치된 EC2 인스턴스들이 있고, 그 인스턴스들에 application 프로그램도 배포되어 있는 상황이라서, 2번째 template 옵션을 선택했습니다. 2번째 옵션을 선택하면, MongoDB 클러스터를 생성할때 bastion 서버로 사용할 기존 EC2 인스턴스들의 subnet 정보를 입력해야하기 때문에, 앱 서버들이 위치한 region 과, subnet 정보를 미리 파악해 놓은 다음, 필요한 옵션 입력화면에서 그 내용들을 정확하게 입력해야만 합니다. (참고로, CloudFormation 의 기본 region 이 virginia 로 자동 변경됩니다. 그걸 눈치채지 못하고 생성했다가 접속이 안되서, 한참동안 뭥미? 했던 경험이 있네요.) 암튼, MongoDB 클러스터가 자신이 원하는 리젼과 subnet 상에 생성되었다면, bastion 서버에 접속후, 다시 몽고서버로 ssh 접속(ssh -i mykey.pem 몽고서버)을 하기 위해서, mykey.pem 도 해당 bastion 서버로 복사합니다. mykey.pem 파일을 bastion 서버에 복사했더라도, 아직은 몽고디비 클러스터로 ssh 접속이나 MongoDB 클러스터 27017 포트로 접속을 할 수 없습니다. 새로운 Security Group 을 생성하여, 아래의 테이블처럼 bastion 서버가 위치하는 주소의 range 를 등록하여 해당 포트 접속이 mongodb://몽고클러스터endpoint:27017 식으로 가능하게 열어줍니다.
규칙 TCP/UDP 포트 IP대역 Comment
Custom TCP Rule TCP 22 10.0.157.0/24 Bastion Private IP 주소에서 SSH 접속용
Custom TCP Rule TCP 27017 10.0.157.0/24 Bastion Private IP 주소에서 Mongo 접속용

AWS S3 의 CSV 파일을 MongoDB 로 import 하기

이전에 3시간가량의 작업으로 생성한 CSV 파일은 CloudFormation 으로 설치한 MongoDB 인스턴스에 ssh 접속 후, 그 인스턴스에 aws cli 를 설치한 다음, 아래 명령으로 리스트를 살펴봅니다. aws s3 ls s3://my-data 그 중 하나를 stdout 으로 덤프하려면 아래의 명령을 사용합니다. aws s3 cp s3://my-data/samples.part_00001 - mongoimport 로 보낼 스트림의 내용이 CSV 와 동일하지 않다면, awk 명령을 사용하여, 원하는 포맷으로 transform 합니다. 가령 예를 들면 아래와 같이 말이죠.
aws s3 cp s3://my-data/samples.part_00001 - | awk -F',' '{ printf("%d,%d,%d,%s,%s,%s,%s,%s,%s,%s,%s,%sn", $2,$3,$4,$5,$6,$12,($4 >= 67) ? "true" : "false",($7 == "N") ? "false" : "true",($8 == "N") ? "false" : "true",($9 == "Rating") ? "true" : "false",$10,$11)}'
스트림의 내용이 원하는 형태로 변경된 것을 확인 했다면, 아래의 예에서 처럼 mongoimport 의 옵션으로 필드와 타입을 지정합니다. 각 옵션에 대한 자세한 설명은 mongoimport 설명문서를 참조하시고요.
aws s3 cp s3://my-data/samples.part_00001 - | awk -F',' '{ printf("%d,%d,%d,%s,%s,%s,%s,%s,%s,%s,%s,%sn", $2,$3,$4,$5,$6,$12,($4 >= 67) ? "true" : "false",($7 == "N") ? "false" : "true",($8 == "N") ? "false" : "true",($9 == "Rating") ? "true" : "false",$10,$11)}' | mongoimport --host 100.100.100.100:27017 --ssl --username admin --password 'whatever!' --authenticationDatabase admin --db database_name --collection mydata --type CSV --fields "user_id.int32(),rater_id.int32(),score.int32(),created_at.date(2006-01-02 15:04:05),updated_at.date(2006-01-02 15:04:05),user_registered_at.date(2006-01-02 15:04:05),is_high.boolean(),is_retry.boolean(),is_unlock.boolean(),is_valid.boolean(),user_gender.string(),user_dob.date(2006-01-02)" --columnsHaveTypes --parseGrace skipRow --numInsertionWorkers 4
MongoDB 클러스터에 AWS m4large 인스턴스 (2CPUs, 8GB 메모리, EBS대역폭 450Mbps) 사용했는데, 10개의 터미널을 열고 동시에 import 를 진행하자 초기에는 정상적으로 동작하다가, 대략 1시간 이후에, 오랜시간동안 freezing 되면서 “Closed Explicitly” 라고, 접속이 자동으로 끊어지는 glitch 가 있었습니다. 하지만 동시에 처리하는 프로세스들의 수를 낮춰 다시 하니까 문제 없이 마이그레이션이 끝나더군요.

MongoDB Atlas 사용하기

MongoDB 와 MongoDB Atlas 에 대한 전반적인 첫 인상은 처음 사용하는 사용자에게도 상당히 user friendly 한 환경이라 느껴졌습니다. Atlas 의 경우 customer service 창으로 던진 질문들에도 응대를 잘 해줍니다. 현재는 M40 인스턴스 타입으로 AWS 서울 리젼에 클러스터를 생성하였습니다. MongoDB Atlas 의 UI 나 지원 문서들의 성숙도가 매우 높아서, 필요한 정보들을 힘들이지 않고 쉽게 찾아 볼 수 있었습니다. 몽고 클러스터 up and running 경험은 한마디로 정말 breeze 입니다. 접속할 AWS EC2 인스턴스의 Security Group 에 필요한 Whitelist IP 대역대를 안내에 따라서 추가하고, 사용하면 끝입니다. mongoimport 를 통해서 Atlas 클러스터로 옮기는 것도 문제가 전혀 없었고요. 이미 앞서 진행했던것과 비슷하게 아래의 임포트 명령을 10개의 터미널 창을 통해 실행시켜서, 몇시간 만에 마이그레이션을 끝낸 다음, index 를 설정하고, 같은 사양의 AWS RDS MySQL 인스턴스와의 속도 비교를 해보았습니다.
aws s3 cp s3://my-data/samples.part_00001 - | awk -F',' '{ printf("%d,%d,%d,%s,%s,%s,%s,%s,%s,%s,%s,%sn", $2,$3,$4,$5,$6,$12,($4 >= 67) ? "true" : "false",($7 == "N") ? "false" : "true",($8 == "N") ? "false" : "true",($9 == "Rating") ? "true" : "false",$10,$11)}' | mongoimport --host MyTest-shard-0/mytest-shard-00-00-fr24m.mongodb.net:27017,mytest-shard-00-01-fr24m.mongodb.net:27017,mytest-shard-00-02-fr24m.mongodb.net:27017 --ssl --username admin --password 'whatever!' --authenticationDatabase admin --db database_name --collection mydata --type CSV --fields "user_id.int32(),rater_id.int32(),score.int32(),created_at.date(2006-01-02 15:04:05),updated_at.date(2006-01-02 15:04:05),user_registered_at.date(2006-01-02 15:04:05),is_high.boolean(),is_retry.boolean(),is_unlock.boolean(),is_valid.boolean(),user_gender.string(),user_dob.date(2006-01-02)" --columnsHaveTypes --parseGrace skipRow --numInsertionWorkers 4

MongDB vs MySQL

아래의 조건으로 비교한 결과입니다.
  • 두 데이터베이스의 하드웨어는 모두 동일하게 r4.large (2CPU/15.25GB/160MB) 인스턴스 사용합니다.
  • 두 데이터베이스의 레코드수는 5억4천만개 정도로 동일합니다.
  • 조회하려는 자료에 두 데이터베이스 모두 인덱스를 걸어두었습니다.
  • MySQL 은 800GB, MongoDB 는 160GB 할당했습니다. (기본 IOPS 는 MySQL 에 유리)
  • MongoDB 테이블에 필요한 데이터가 모두 있는터라, 공평한 속도 비교를 위해 MySQL 도 조인 사용하지 않습니다.
  • 일정 범위에 속하는 사용자 아이디 1,000개에 대해서 loop 를 돌면서, 각각의 사용자에 할당된 최근 500개 점수의 합을 구하는 로직의 실행시간비교 테스트입니다.
  • MySQL 의 경우 각각의 사용자의 최근 레코드 500개를 가져와서 실제 합산은 application layer 에서 Laravel 이 수행하게 되고, MongoDB 의 합산은 몽고의 aggregation framework 에서 즉, 쿼리 레벨에서 처리합니다.
  • 똑같은 테스트를 연속해서 2번씩 돌리면서 시간을 측정하여, 캐쉬로 인한 감소된 실행시간도 비교합니다.
round MongoDB MySQL
#1 (1012999~1013999) 39.83 초, 4.61초 80.38 초, 14.25 초
#2 (1010999~1011999) 34.36 초, 4.79 초 105.34 초, 14.92 초
#3 (1000999~1001999) 33.06 초, 4.59 초 74.80 초, 15.06 초
#4 (990999~991999) 30.71 초, 3.80 초 60.60 초, 6.50 초
#5 (967900~968900) 38.30 초, 4.83 초 57.13 초, 14.11 초
#6 (933199~934199) 31.04 초, 3.67 초 84.38 초, 14.34 초
#7 (900999~901999) 39.91 초, 4.70 초 80.40 초, 15.40 초
#8 (840100~841100) 35.94 초, 4.52 초 71.26 초, 13.47 초
#9 (543210~544210) 47.01 초, 5.76 초 144.36 초, 14.71 초
#10 (443210~444210) 44.89 초, 5.21 초 117.21 초, 14.56 초
이렇게 직접 성능 비교를 해보니, 이번에 사용하는 use case 에 MongoDB 로의 마이그레이션이 나쁘지 않은 결정이라고 생각들었습니다. 다음 번 글에서는 MongoDB 의 강력한 aggregation framework 에 대하여 설명을 해보겠습니다.
Posted by admin in Backend, 1 comment

멀티 프로젝트에서 라라독(Laradock) 사용

이 문서의 목적은 Laradock 을 설치한 로컬 도커환경에서, 복수의 라라벨 프로젝트간에 Guzzle/Curl 사용을 할 수 있도록 설정하는 것입니다. 사용자 환경은 Docker for Mac 18.03 이후 버전을 사용한다고 가정합니다. Laradock 의 기본적인 멀티 프로젝트 설정에 관련된 내용은 Laradock 공식문서의 Setup for Multiple Projects 편을 참고하여, 아래 폴더 구조로 멀티 프로젝트들을 구성합니다. Laradock 공식문서의 설명대로 설치한 경우, 각각의 서비스를 독립적으로 동작시키는 것에는 문제가 없으나, 복수의 라라벨 프로젝트 간에 서로 Guzzle/Curl 통신을 할 수가 없어서, 마이크로 서비스 구조로 만들어진 멀티 프로젝트간의 상호 API 호출은 불가능 합니다. 일단, 사용하려는 라라독 폴더 구조가 아래와 같다면,
+ laradock
+ project-1
+ project-2
맥의 /etc/hosts 파일 하단에, 필요한 도메인들의 주소를 localhost IP 주소로 추가합니다.
127.0.0.1  project-1.test
127.0.0.1  project-2.test
그런다음, 아래와 같이 두 서비스에서 필요한 모든 도커 컨테이너들을 실행합니다. (실제로는 이처럼 자주 사용하면서 복잡한 명령어의 경우 alias 로 설정해두면 좋겠죠.)
docker-compose up -d nginx php-fpm php-worker workspace mysql mongo redis beanstalkd beanstalkd-console elasticsearch kibana
정상적으로 설치가 되었다면, 프로젝트 1 (http://project-1.test) 및 프로젝트 2 (http://project-2.test) 에 모두 접속이 잘될 것입니다. 하지만, 아래의 명령을 이용해서 workspace 컨테이너에 들어간 다음,
docker-compose exec workspace bash
커맨드 라인에서 curl project-1.test 라고 입력하면, 안타깝게도 [curl] 7: Failed to connect to project-1.test port 80: Connection refused 라는 오류 메시지가 나고, 접속에 실패하는 걸 볼 수 있습니다. Docker for Mac 의 공식 문서를 보면, 18.03 이후 버전부터는 network access 가 가능한 컨테이너의 경우 host.docker.internal 라는 이름의 특별 DNS 레코드가 존재하는 걸 알 수 있는데, 이 레코드가 가리키는 IP 주소가 바로 그 컨테이너에서, 호스트 상의 다른 서비스로의 연결을 원할 때 사용해야하는 IP 주소입니다. 실제로 dig 명령을 사용해보면 192.168.65.2 처럼 Docker subnet 범위 안에 존재하는 하나의 IP 주소가 설정되어 있는 걸 알 수 있습니다. 이 주소는 고정 IP 주소가 아니라, 변동될 수 있는 IP 주소라고 합니다만, 한번 자동으로 설정된 이후, 매번 유동적으로 변동하는 것 같지는 않습니다. 위에서 언급했듯, workspace 컨테이너에 들어가서, 다음과 같이 설정된 IP 주소를 확인해 봅니다.
dig host.docker.internal
주) dig 명령은 기본 라라독의 workspace 컨테이너에 존재하지 않는 명령입니다. 따라서, workspace 의 Dockerfile 파일을 열고 적당한 곳에 아래와 같은 라인을 추가한 다음, workspace 컨테이너를 다시 리빌드하면 사용할 수 있습니다.
USER root
RUN apt-get -y install dnsutils
workspace 컨테이너 리빌드는 다음 명령을 사용합니다. (완전히 이미지 다운로드부터 시작하려면 –no-cache 옵션을 추가합니다.)
docker-compose build workspace
이런식으로 자신의 도커 환경에 설정된, 호스트 IP 주소를 확인했다면, 다음과 같이 docker-compose.yml 파일의 마지막 위치에 아래의 라인을 추가합니다.
HOST_IP_ADDRESS=192.168.65.2
(위의 192.168.65.2 라는 IP주소 값은 각자의 환경마다 다를 수 있습니다.) 이제 남은 작업은, 호스트 상의 다른 서비스로의 접근이 필요한 컨테이너들의 /etc/hosts 파일에 바로 이전에 HOST_IP_ADDRESS 로 지정한 주소를 추가하는 일입니다. docker-compose.yml 안에 각각의 컨테이너 정의 부분이 있는데, 필요한 컨테이너 정의 부분에 extra_hosts 옵션값을 추가하면 됩니다. 예를 들어, PHP Worker 정의 부분은 기본적으로 아래와 같지만
### PHP Worker ############################################
    php-worker:
      build:
        context: ./php-worker
        args:
          - PHP_VERSION=${PHP_VERSION}
          - INSTALL_PGSQL=${PHP_WORKER_INSTALL_PGSQL}
      volumes:
        - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}
        - ./php-worker/supervisord.d:/etc/supervisord.d
      depends_on:
        - workspace
      extra_hosts:
        - "dockerhost:${DOCKER_HOST_IP}"
      networks:
        - backend
위의 내용을 아래와 같이 수정합니다.
### PHP Worker ############################################
    php-worker:
      build:
        context: ./php-worker
        args:
          - PHP_VERSION=${PHP_VERSION}
          - INSTALL_PGSQL=${PHP_WORKER_INSTALL_PGSQL}
      volumes:
        - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}
        - ./php-worker/supervisord.d:/etc/supervisord.d
      depends_on:
        - workspace
      extra_hosts:
        - "dockerhost:${DOCKER_HOST_IP}"
        - "project-1.test:${HOST_IP_ADDRESS}"
        - "project-2.test:${HOST_IP_ADDRESS}"
      networks:
        - backend
저같은 경우에는, workspace, php-fpm, php-worker 이렇게 3개 컨테이너의 extra_hosts 옵션을 동일하게 변경하였는데, 각자 사용환경 조건에 따라 적당히 변경하면 되겠죠. 라라독의 기본 네트웍 드라이버 설정은 bridge 네트웍 드라이버를 사용하고 frontendbackend 라고 명명되어 있습니다. 컨테이너 안에서 /sbin/ip route 명령을 쳐보면, 아래의 내용처럼 172.24.x.x172.25.x.x 대역 범위을 사용하며 172.24.0.1172.25.0.1 을 게이트웨이로 사용하여, 모든 아웃바운드 트래픽을 포워딩하는 걸 알 수 있습니다.
default via 172.25.0.1 dev eth0
172.24.0.0/16 dev eth1  proto kernel  scope link  src 172.24.0.2
172.25.0.0/16 dev eth0  proto kernel  scope link  src 172.25.0.2
하지만, HOST_IP_ADDRESS 에서 사용하는 192.168.x.x IP 주소와 다른 네트웍에 속해 있기 때문에, 호스트 상의 다른 서비스를 연결하기 위해서는 위와 같은 설정이 필요합니다.
Posted by admin in Backend, 0 comments