http로 배포를 했었는데요,,, seo최적화 및 클립보드 링크복사기능을 위해서 https가 필요했는데,,,
그 과정이 꽤나 험난했어서 ssl키 발급부터 ssl키를 프로젝트 파일에 넣고, dockerfile을 만든 후 이미지를 업로드해서 배포하기 까지의 과정을 살펴보려 합니다...
1. 사전작업
우선, Https로 배포하기 위해 ec2의 인바운드규칙에서 80포트는 접근하지 못하게 하였고 443으로만 접근하게 해 두었으며,ec2 바깥에 로드밸런서로 80포트(Http)로 접근시 443(https)로 리다이렉트 되도록 설정을 해 둔 상태입니다.
그리고 탄력적 ip로 고정 public ip를 사용하였고 이 public Ip를 도메인주소의 A레코드에 연결해 두었습니다.
2. 배포환경
저희 프로젝트의 배포환경은 프론트엔드는 Next.js, 백엔드는 Spring, 그리고 이걸 dockerfile을 만들어서 도커이미지를 생성 후 hub에 올리고 vm에서 pull받아서 배포를 하는 형태였습니다.
이 과정에서 프론트엔드에서 ssr(server side rendering)때문에 build시에 캐싱데이터를 검증하는 과정에 최초 빌드시에는 캐시데이터가 없어서 백에서 받아오는 과정이 있는데 여기에서 서버가 켜져있지 않는경우 프론트의 Dockerfile자체가 빌드되지 않는 문제가 있었습니다. HTTP에서는 서버를 켜놓고 프론트파일을 빌드해서 이 문제를 해결했습니다!
그런데 https로 배포하는 과정에서 여러가지 문제가 발생했었는데 여기서 부터 설명을 해볼까합니다!ㅎㅎ
본격적인 저희 프로젝트에서 발생한 문제들에 앞서, 저희 프로젝트와 비슷한 환경에서 배포한 레퍼런스를 찾기 어려워서 저는 https의 동작원리부터 익히고 가야겠다는 생각에 https기초부터 다시 공부를 하고 시작을 했습니다! 관련내용은 2024.01.25 - [네트워크] - [네트워크] HTTPS와 TLS(SSL) 이 포스팅을 참고해보시면 좋을 것 같습니다!
위의 포스팅을 참고해보면 https배포를 위해서는 ssl-key가 필요하다는 걸 알 수 있습니다. 저희도 그래서 ssl-key를 발급받았습니다... 그것도 매우 여러번,,,ㅎ
ssl-key는 CA에서 발급받아야 하는데요..! 저희는 cerbot을 통해서 let's encrypt에서 인증서를 발급받았습니다!
그럼 발급 받는 과정에서 생겼던 문제부터 정리해보도록 하겠습니다.
(1) cerbot에서 발급받은 키 어디에 둬야함???
레퍼런스들을 참고해보았을 때 cerbot에서 키를 발급받는건 보통 nginx를 설치하고 vm머신에서 발급을 받았습니다.
그런데,,,, 저희 배포환경에서는 nginx를 프록시서버로 두는 것도 아니고 굳이 nginx가 필요없이 aws의 로드밸런서를 vm바깥에 두고 80포트로 들어오는 요청은 443으로 리다이렉트 시키는 구조라 필요가 없었습니다.
그리고 스프링 부트 내에서 서버를 시작할때 https라는걸 인식시키기 위해서는 application.properties나 application.yml에
server.port=443
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-type=PKCS12
server.ssl.key-store-password=비밀번호
이런식으로 넣어줘야 하는데 vm에 설치를 하면 안된다는걸 늦게 깨달아버려서.. 다시 로컬로 옮기려고 하는 과정에서 많은 문제들이 발생했습니다... 그래서 결국 다시 발급받았습니다. springboot를 사용중이라면 로컬에서 받으세요...
다시 언급되겠지만 Next.js에서도 dockerfile을 빌드하기 위해서 key가 필요했습니다.. 로컬에서 받으세요....
(2) cerbot에서 키가 발급 안됨
그래서 로컬에서 받으러 왔습니다!
많은 레퍼런스들을 찾아봤을 때 cerbot으로 ssl인증서를 발급 받는 방법은 3가지가 있었습니다.
저는 여기에서 1,2번을 도전했는데 실패해서 다른 방법은 없나? 하고 openssl이라는게 있길래 이걸로 SSL인증서를 발급 받아서 시도해보았습니다.
지금와서 다시 돌아보니 openssl은 사설인증서라서 배포시에 문제가 발생하는게 당연했는데....
그 과정을 보여드리겠습니다 ㅎ
1) openssl로 사설 인증키 발급
https://goldsony.tistory.com/223
이 두개의 레퍼런스를 참고해서 self-signed-ssl키를 만들었습니다.
아마 .pem파일 두개와 .p12파일이 만들어졌을 겁니다. 생성된 위치를 찾아서 로컬에 있는 서버 파일의 루트경로에 keystore.p12파일을 넣어주세요..
그래서 서버파일을 실행시켜보니!!!! 드디어 https로 작동을 합니다! (위에서 언급했던 application.properties 코드를 넣어주셔야합니다)
이렇게 로컬에서 켜놓고 도커이미지도 만들고~ hub에 올리고~ vm에서 pull받아서 실행시켰습니다! 됩니다!!
이제 Next.js만 빌드해주면 되겠네용 ㅎㅎ 그쵸??
BUT!!!!
docker build를 하면 저희 프로젝트의 nextjs에서 SSR(servers side rendering)인 /api/statistics 부분에서 에러가 납니다...
그래서 self-signed-ssl키로 받았던 cert.pem과 key.pem을 next.js의 root경로에 넣어주었습니다.
이렇게 빌드를 하면 성공할 것 같았는데...
=> ERROR [builder 4/4] RUN npm run buil 52.8s
------
> [builder 4/4] RUN npm run build:
1.816
1.816 > ah-hew@0.1.0 build
1.816 > next build
1.816
3.582 ⚠ Invalid next.config.js options detected:
3.583 ⚠ Unrecognized key(s) in object: 'server'
3.583 ⚠ See more info here: https://nextjs.org/docs/messages/invalid-next-config
3.603 Attention: Next.js now collects completely anonymous telemetry regarding usage.
3.603 This information is used to shape Next.js' roadmap and prioritize features.
3.603 You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
3.603 https://nextjs.org/telemetry
3.603
3.761 ▲ Next.js 14.0.4
3.761 - Environments: .env.local
3.762
3.762 Creating an optimized production build ...
32.75 ✓ Compiled successfully
32.75 Linting and checking validity of types ...
33.85 ⨯ ESLint: Failed to load config "airbnb" to extend from. Referenced from: /usr/src/app/.eslintrc.json
43.85 Collecting page data ...
49.18 Generating static pages (0/10) ...
Generating static pages (2/10)
Generating static pages (4/10)
51.08 TypeError: fetch failed
51.08 at Object.fetch (node:internal/deps/undici/undici:11730:11)
51.08 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
51.08 cause: Error: self-signed certificate
51.08 at TLSSocket.onConnectSecure (node:_tls_wrap:1674:34)
51.08 at TLSSocket.emit (node:events:514:28)
51.08 at TLSSocket._finishInit (node:_tls_wrap:1085:8)
51.08 at ssl.onhandshakedone (node:_tls_wrap:871:12)
51.08 at TLSWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
51.08 code: 'DEPTH_ZERO_SELF_SIGNED_CERT'
51.08 }
51.08 }
51.08 TypeError: fetch failed
51.08 at Object.fetch (node:internal/deps/undici/undici:11730:11)
51.08 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
51.08 cause: Error: self-signed certificate
51.08 at TLSSocket.onConnectSecure (node:_tls_wrap:1674:34)
51.08 at TLSSocket.emit (node:events:514:28)
51.08 at TLSSocket._finishInit (node:_tls_wrap:1085:8)
51.08 at ssl.onhandshakedone (node:_tls_wrap:871:12)
51.08 at TLSWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
51.08 code: 'DEPTH_ZERO_SELF_SIGNED_CERT'
51.08 }
51.08 }
51.08
51.08 Error occurred prerendering page "/statistics". Read more: https://nextjs.org/docs/messages/prerender-error
51.08 TypeError: fetch failed
51.08 at Object.fetch (node:internal/deps/undici/undici:11730:11)
51.08 at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Generating static pages (7/10)
✓ Generating static pages (10/10)
51.90
51.90 > Export encountered errors on following paths:
51.90 /statistics/page: /statistics
52.44 npm notice
52.44 npm notice New minor version of npm available! 10.2.3 -> 10.3.0
52.44 npm notice Changelog: <https://github.com/npm/cli/releases/tag/v10.3.0>
52.45 npm notice Run `npm install -g npm@10.3.0` to update!
52.45 npm notice
------
Dockerfile:18
--------------------
16 | COPY --from=deps /usr/src/app/node_modules ./node_modules
17 | COPY . .
18 | >>> RUN npm run build
19 |
20 | # Production image, copy all the files and run next
--------------------
ERROR: failed to solve: process "/bin/sh -c npm run build" did not complete successfully: exit code: 1
View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/x5qqkxb8ubhrei9hhcvjbo7ps
BUT!!! build failed...ㅠㅠ
실패한 이유는 self-signed-ssl Key라서 안된답니다...
그래서 열심히 찾아보았더니 Docker file에서 설정을 self-signed-ssl key를 무시하는 방법이 있다고 해서 그걸 추가해주었습니다.
next.js dockerfile
ENV NODE_TLS_REJECT_UNAUTHORIZED=0
FROM node:20.10.0-alpine AS base
ENV NODE_TLS_REJECT_UNAUTHORIZED=0
# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /usr/src/app
# Install dependencies based on the preferred package manager
COPY package.json package-lock.json ./
RUN npm i -y
RUN rm -rf ./.next/cache
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /usr/src/app
COPY --from=deps /usr/src/app/node_modules ./node_modules
COPY . .
RUN npm run build
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /usr/src/app
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /usr/src/app/public ./public
COPY --from=builder --chown=nextjs:nodejs /usr/src/app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /usr/src/app/.next/static ./.next/static
COPY cert.pem .
COPY key.pem .
USER nextjs
# EXPOSE 3000
CMD ["node", "server.js"]
아 그리고 https로 설정하기 위해서 next.config.js도 설정을 해주어야 합니다!
next.config.js
const fs = require('fs');
const path = require('path');
const nextConfig = {
server: {
https: {
key: fs.readFileSync(path.join(__dirname, 'key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
},
},
reactStrictMode: false,
async rewrites() {
return [
{
source: '/naver/:path*',
destination: 'https://openapi.naver.com/:path*',
},
];
},
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'media1.jjalkey.com',
},
{
protocol: 'http',
hostname: 'ahwhew.shop',
},
{
protocol: 'https',
hostname: 'example-bucket-seeun.s3.ap-northeast-2.amazonaws.com',
},
],
},
output: 'standalone',
};
module.exports = nextConfig;
이렇게 해서 드디어!! 프론트엔드도 docker build 성공!!!
뭔가 잘 될 것 같은 기분에 설렜습니다 ㅎㅎ 아까전에 무시했던 부분때문에 뭔가 찜찜하긴 했지만,,,(배포하면 안될거라는걸 미리 알았나?)
아무튼 이렇게 nextjs도 이미지 만들어서 hub에 올리고~ vm에서 Pull 받아서 실행해보았습니다!
오오 화면이 뜬다!!! 자 통계페이지 들어가보자~~
역시나 안됩니다ㅎ 역시 아까 무시했던 부분 때문에 안됩니다. self-signed-key라서 안된대요ㅠㅠ
GET https://ahwhew.com/api/statistics
200
1040 ms
Warning: Self signed certificate
Network
Request Headers
User-Agent: PostmanRuntime/7.36.0
Accept: */*
Postman-Token: 7d4fcc92-53eb-41d7-9354-c118e6d4fd84
Host: ahwhew.com
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Response Headers
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 13 Jan 2024 12:39:21 GMT
Keep-Alive: timeout=60
Connection: keep-alive
Response Body
[{"date":"2023-12-18","averagePositive":16.6954131997,"averageNegative":44.40761578568889,"averageNeutral":38.89697322717779,"count":9},{"date":"2023-12-19","averagePositive":50.018534544000005,"averageNegative":49.92605748327,"averageNeutral":0.05540883371,"count":4},{"date":"2023-12-20","averagePositive":0.0069567429333333335,"averageNegative":88.87453264288888,"averageNeutral":11.11851156177778,"count":9},{"date":"2023-12-21","averagePositive":7.204530048271431,"averageNegative":70.82278236964285,"averageNeutral":21.972687737392857,"count":28},{"date":"2023-12-22","averagePositive":9.114511329290908,"averageNegative":58.46822950122728,"averageNeutral":32.417258590045456,"count":22},{"date":"2023-12-23","averagePositive":25.88189243399545,"averageNegative":47.610279138818186,"averageNeutral":26.507828555868187,"count":22},{"date":"2023-12-24","averagePositive":25.001821663650002,"averageNegative":71.37141074499999,"averageNeutral":3.62676722775,"count":4},{"date":"2023-12-25","averagePositive":32.454962104423075,"averageNegative":28.65462591366154,"averageNeutral":38.89041209790769,"count":13},{"date":"2023-12-26","averagePositive":16.84914102585,"averageNegative":33.41882819433334,"averageNeutral":49.73203249666667,"count":6},{"date":"2023-12-27","averagePositive":5.035972668526829,"averageNegative":29.526654492146342,"averageNeutral":65.4373727090488,"count":41},{"date":"2023-12-28","averagePositive":6.036256385420338,"averageNegative":23.537672245625423,"averageNeutral":70.42607123609493,"count":59},{"date":"2023-12-29","averagePositive":22.13235843878154,"averageNegative":26.90003248042616,"averageNeutral":50.96760882392461,"count":65},{"date":"2023-12-30","averagePositive":25.667546830353846,"averageNegative":29.95237661353846,"averageNeutral":44.38007475538461,"count":13},{"date":"2023-12-31","averagePositive":17.109149854736668,"averageNegative":27.944464638873335,"averageNeutral":54.94638603012334,"count":30},{"date":"2024-01-01","averagePositive":18.720691893362503,"averageNegative":10.78656341896875,"averageNeutral":70.49274468188752,"count":16},{"date":"2024-01-02","averagePositive":21.33612188186957,"averageNegative":19.62166362417174,"averageNeutral":59.042214129564144,"count":46},{"date":"2024-01-03","averagePositive":14.68278041519091,"averageNegative":57.341724380827266,"averageNeutral":27.97549444288182,"count":11},{"date":"2024-01-04","averagePositive":4.2151028461,"averageNegative":45.762824624000004,"averageNeutral":50.02207218867272,"count":11},{"date":"2024-01-05","averagePositive":40.545262331052164,"averageNegative":21.829430473408692,"averageNeutral":37.625307252373915,"count":23},{"date":"2024-01-06","averagePositive":0.0598562998,"averageNegative":37.55412086,"averageNeutral":62.386022397459996,"count":5},{"date":"2024-01-07","averagePositive":35.015542921727274,"averageNegative":50.91741807060309,"averageNeutral":14.06703974375818,"count":11},{"date":"2024-01-08","averagePositive":1.2724256648,"averageNegative":65.26917418583334,"averageNeutral":33.458401401500005,"count":6},{"date":"2024-01-09","averagePositive":39.7838974524,"averageNegative":59.133211673999995,"averageNeutral":1.082889697,"count":3},{"date":"2024-01-10","averagePositive":76.346596,"averageNegative":15.569013,"averageNeutral":8.084394,"count":1},{"date":"2024-01-12","averagePositive":43.84982,"averageNegative":25.329308218,"averageNeutral":30.820874000000003,"count":2},{"date":"2024-01-13","averagePositive":53.8115209195,"averageNegative":25.005640385,"averageNeutral":21.18283739575,"count":4}]
Postman으로 테스트했을 때 입니다.
응답바디는 잘 찍히는데 보안 경고가 떴습니다. Self Signed Certificate...
이렇게... 결국 실패....
cerbot으로 받는게 너무 안돼서 self-signed-key로 어떻게든 해보려했는데 안되네요...
cerbot으로 받는 나머지 한가지 방법이 남았죠???
다음 포스팅에서는 그걸로 ssl key를 받아서 배포하는 과정까지 알려드리도록 하겠습니다! (이미 힌트를 얻으신 분들이 많겠지만,,,)
'프로젝트 > AHWHEW' 카테고리의 다른 글
[AHWHEW] Next.js+Springboot+Docker배포시 발생한 트러블 슈팅 정리-(2)cerbot으로 ssl-key발급부터 Docker PortMapping까지(성공기) (0) | 2024.01.29 |
---|