AWS의 CDN 제품인 CloudFront는 원본 서버에서 제공하는 기본 오브젝트를 지정할 수 있는 기능을 제공하고 있습니다. 보통은 원본 서버에서 사용하는 웹 서버에서 기본 오브젝트를 지정하는 경우가 많지만 S3 등 다른 제품을 함께 사용할 때는 기본 오브젝트를 지정할 수 있는 방법이 없기 때문에 파일명을 지정할 수 있는 기능이 필요합니다.
기본 루트 오브젝트 설정 Set Default Root Object
CloudFront Distribution의 기본 설정 탭에서 기본 루트 오브젝트를 설정할 수 있습니다. Distribution 목록에서 작업하려는 Dist ID를 선택하여 상세 화면으로 진입합니다. 상단 탭중 첫번째 탭인 General 탭으로 이동합니다. Settings 섹션의 `Edit` 버튼을 눌러 상세 설정 화면으로 진입해 봅니다.
Edit 화면의 중간 정도를 살펴보면 `Default root object - optional` 항목을 찾으실 수 있습니다. 이곳에 지정된 파일 이름이 CloudFront를 통해 접근시 사용되는 기본적인 파일 이름이 됩니다. 가령 www.iamnopd.com 이라는 도메인이 있다고 가정했을 때, 브라우저에 www.iamnopd.com 만 입력해도 알아서 index.html 을 찾아 사용자 브라우저로 응답을 하게 되는 거죠.
다양한 경로에 대한 기본 오브젝트 설정
그런데 문제가 있습니다. `Default root object`에 지정한 파일 이름은 진짜, 레알, 루트 경로에 대한 요청에 대해서만 적용되는 한계가 있습니다. 가령 원본이 S3 버킷이라고 했을 때, 버킷의 최상위 경로에도 index.html 이 있고, 버킷에 생성되어 있는 api 폴더의 하위에도 index.html 이 있다고 해봅시다.
원본의 서로 다른 경로를 특정 사용자 Path의 루트로 바인딩 하기 위해서는 (아시겠지만) 아래와 같이 동일 원본을 Origin Path만 분기하여 등록하면 됩니다. 이 구성과 Default root object 의 조합은 /api 경로에 대해서 적용되지 않는다는 상황으로 이해하지면 될 것 같습니다. 그러면 어떻게 하면 될까요?
CloudFront Function을 이용한 경로 조작
S3에서 뭔가를 하면 되지 않을까? 하고 생각했을지도 모르지만 그것보다는 CloudFront Function을 쓰면 간단하게 구현할 수 있습니다. 아시겠지만 Lambda@Edge를 쓰는 것보다 CloudFront Function을 쓰는 것이 사용도 간편하고(=기능이 다소 적고) 비용도 저렴합니다. 묻지도 따지지도 말고 바로 코드를 보겠습니다.
function handler(event) {
var pathFrom = /^\/api$/g
var pathTo = '/api/index.html'
var pathDecided = '';
var request = event.request;
pathDecided = request.uri;
if (pathDecided.match(pathFrom) != null) {
pathDecided = pathDecided.replace(pathFrom, pathTo);
}
request.uri = pathDecided;
return request;
}
코드는 무척 간단합니다. 간단한 Regex 를 이용하여 들어온 URI (request.uri)를 단순히 문자열 비교한 후에 실제로 원본이 받게될 URI 를 변경해서 return 해주는 코드입니다. 요청량이 많아도 크게 부담스러운 금액이 나오지는 않을것 같습니다. 간단하지만 Default root object가 단일 경로에 대해서만 적용되는 제약을 회피(?)하는 방법을 살펴봤습니다 :-)
테스트도 참 쉽죠? 원하는 결과를 테스트를 통해 얻었다면, 만든 함수를 적용하고자 하는 Distribution의 Behavior의 Viewer Request 함수로 지정하면 됩니다. 끝!
간만에 Swift 환경으로 넘어오니 묵혀있던 애플 M1 환경에서의 에러 공습이 시작되었습니다. cocoapod 설치까지는 잘 진행되었으나 필요한 의존성을 Podfile에 기술하고 설치하는 작업에서 문제가 생겼습니다. 설치하고자 한 의존성은 Alamofire 였습니다.
# Podfile
target 'ch6' do
use_frameworks!
pod 'Alamofire', '~> 5.4'
end
에러 메세지도 한번 보고나서 트러블 슈팅을 진행해 보겠습니다. M1 을 쓰기 전이었다면 다른 케이스를 먼저 의심했겠지만, M1을 쓰기 시작한 후부터 이런 단순한 작업에서 에러가 발생하면 일단 M1 환경 문제로 보는 습관이 생겼습니다 ㅎㅎ
% pod install
Analyzing dependencies
/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi/library.rb:275: [BUG] Bus Error at 0x00000001012d4000
ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.arm64e-darwin20]
-- Crash Report log information --------------------------------------------
See Crash Report log file under the one of following:
* ~/Library/Logs/DiagnosticReports
* /Library/Logs/DiagnosticReports
for more details.
Don't forget to include the above Crash Report log file in bug reports.
-- Control frame information -----------------------------------------------
c:0049 p:---- s:0307 e:000306 CFUNC :attach
c:0048 p:0258 s:0301 e:000300 METHOD /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi/library.rb:275
c:0047 p:0050 s:0281 e:000280 CLASS /Library/Ruby/Gems/2.6.0/gems/ethon-0.14.0/lib/ethon/libc.rb:17
...
..
..
...
653 /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi/autopointer.rb
654 /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi/variadic.rb
655 /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi/enum.rb
656 /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi/version.rb
657 /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi/ffi.rb
658 /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi.rb
[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: https://www.ruby-lang.org/bugreport.html
[IMPORTANT]
Don't forget to include the Crash Report log file under
DiagnosticReports directory in bug reports.
zsh: abort pod install
이제 정답을 찾기 위해 열심히 구글링을 했고 두가지 정도의 방법을 찾았습니다.
방법#1. cocoapod 를 실행하는 터미널을 Rosetta 로 실행하기
M1 에서 호환성 문제를 해결하기 위해 가장 먼저 사용해 볼 수 있는 것이 Rosetta 입니다. iTerm 을 터미널 클라이언트로 쓰고 있기 때문에 "파인더 > 응용프로그램"에서 iTerm을 찾아 속성에서 Rosetta 를 켜주면 됩니다.
이제 동작중인 터미널을 종료하고 pod install 을 다시 수행해봅니다. 하지만 여전히 에러가 발생합니다.
% pod install
Analyzing dependencies
[!] Automatically assigning platform `iOS` with version `15.0` on target `ch6` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.
Traceback (most recent call last):
45: from /usr/local/bin/pod:23:in `<main>'
44: from /usr/local/bin/pod:23:in `load'
43: from /Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/bin/pod:55:in `<top (required)>'
42: from /Library/Ruby/Gems/2.6.0/gems/cocoapods-1.11.2/lib/cocoapods/command.rb:52:in `run'
41: from /Library/Ruby/Gems/2.6.0/gems/claide-1.0.3/lib/claide/command.rb:334:in `run'
...
..
..
...
1: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require': dlopen(/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi_c.bundle, 9): no suitable image found. Did find: (LoadError)
/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi_c.bundle: mach-o, but wrong architecture
/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi_c.bundle: mach-o, but wrong architecture - /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi_c.bundle
36: from /usr/local/bin/pod:23:in `<main>'
35: from /usr/local/bin/pod:23:in `load'
...
..
..
...
3: from /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi.rb:2:in `<top (required)>'
2: from /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi.rb:5:in `rescue in <top (required)>'
1: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require': dlopen(/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi_c.bundle, 9): no suitable image found. Did find: (LoadError)
/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi_c.bundle: mach-o, but wrong architecture
/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi_c.bundle: mach-o, but wrong architecture - /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.3/lib/ffi_c.bundle
에러 메세지를 참고하여 ffi에 문제가 있다는 것을 확인하고 재설치를 진행해 봅니다. 이후 다시 pod install 을 수행하면 문제가 해결된다고 합니다. 제 경우는 아쉽게도...
% sudo gem install ffi
% pod install
방법#2. Ruby FFI 라이브러리 업데이트 후 재설치
에러 메세지에 아키텍쳐 관련한 내용이 계속 나오는 것에 착안하여 사람들이 올려둔 방법중 아키텍쳐 관련된 것들을 사용해 보았습니다. ffi를 설치할 때 아키텍쳐를 x86_64 로 지정하여 설치하는 방식입니다. (M1은 arm 기반이라 arch 명령을 수행해보면 arm64 라고 나옵니다)
% sudo arch -x86_64 gem install ffi
Password:
Fetching ffi-1.15.4.gem
Building native extensions. This could take a while...
Successfully installed ffi-1.15.4
Parsing documentation for ffi-1.15.4
Installing ri documentation for ffi-1.15.4
Done installing documentation for ffi after 9 seconds
1 gem installed
설치만 하면 pod install 이 동작할까요? 아닙니다. 그냥 pod install 명령을 사용하면 아키텍쳐가 arm64 로 인식되기 때문에 여전히 아키텍쳐 관련한 호환성 문제가 발생합니다. 대신 명령 앞에 아키텍쳐를 지정해서 실행해 보도록 하겠습니다.
% arch -x86_64 pod install
Analyzing dependencies
Downloading dependencies
Installing Alamofire (5.4.4)
Generating Pods project
Integrating client project
Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.
근래에 가장 각광받는 자격시험이 쿠버네티스 Kubenetes 관련된 자격증일 것 같습니다. 리눅스 파운데이션에서 주관하는 쿠버네티스 시험은 크게 3가지 종류인데요, 최근 모 조사기관에서 발표했던 내용에 따르면 모든 자격 시험들 중 CKA(Certified Kubenetes Administrator)에 대한 관심이 가장 많다고 합니다.
쿠버네티스 관련된 시험들은 생각보다 가격이 좀 있기 때문에 할인 쿠폰이 유통(?)될 때 미리 시험을 등록해 두는 것이 좋다고 합니다. 시험을 등록하면 목표 일정을 설정하는 효과가 있으니... k8s를 학습해 나가는데 좋은 모티베이션이 될 것이라고 생각됩니다. 시험 등록일 기준으로 1년간 리스케쥴링이 가능하고 무료 재시험도 한차례 제공되니 사실상 부담도 없을 것 같습니다.
이번에 유통되는 할인 코드는 SCHOOL25 입니다. 리눅스 파운데이션의 시험 등록 페이지에서 쿠폰 코드 Coupon Code 항목에 입력해 주시면 됩니다. 환율이 요즘 급격히 변화하고 있어서 전표 매입 타이밍에 따라 실제 비용은 좀 차이가 생기겠지만 1160원 기준으로 계산해 보면 33만원 정도인 것 같네요. 할인 받아도 싸지는 않은 느낌입니다 ㅎㅎ
여튼, 쿠버네티스 자격시험 준비하고 계신다면 이번 할인 쿠폰을 잘 활용해 보시기 바랍니다. 꾸준히(?) 할인 쿠폰이 나오니 Apply 를 눌러서 쿠폰 적용이 안된다면 한달 정도 여기저기 또 검색해 보시면 할인 쿠폰을 발견하실 수 있을거라 생각합니다. 마지막으로 개인적으로 추천드리는 k8s 강의 링크 하나 드려 봅니다. 강의도 할인 중이라 대략 16,000원 정도로 저렴하네요!
(추가. 2022.09) 참고로, 저 역시 위 강의를 듣고 2022년 9월 시험에 합격했습니다. 시험 합격 후기와 시험 꿀팁, 가상 시험 환경에 대한 이야기는 다음의 포스팅에서 간략하게 정리를 해보았습니다. 시험을 준비하고 계시다면 조금이나마 도움이 될 것 같습니다!