예전에는 제로보드, 워드프레스 같은 CMS(콘텐츠 매니지먼트 시스템)를 주로 사용해서 카페(게시판)나 블로그 등을 만들어 서비스하는 것이 대세였습니다. 현재는 Frontend의 발달로 인해 백앤드 위주로 서비스되는 Headless CMS가 부상을 하고 있고 그 중심에 STRAPI가 있습니다.
STRAPI
STRAPI는 쉽게 말하면 워드프레스 같은 CMS에서 백앤드쪽의 부분만 담당하는 서비스라고 보시면 됩니다. 프론트앤드는 react.js로 개발하든, vue.js로 개발하든 알아서하고 백앤드는 STRAPI에서 제공하는 RESTful or GraphQL로 데이터를 주고받도록 하는 것입니다. 그렇게 확보된 콘텐츠는 STRAPI에서 관리할 수 있습니다. 자세한 설명은 아래의 링크를 참조하세요.
시놀로지 도커에서 STRAPI V4 서비스하기
시놀로지에 보면 도커를 사용할 수 있는 기능이 있습니다.
이 도커를 이용해서 STRAPI를 서비스할 수 있습니다. 그런데 한 가지 작은 문제가 있습니다. 작년까지만 해도 STRAPI에서 도커이미지를 만들어서 도커 허브에 올려줘서 쉽게 사용할 수 있었는데, 지금 4 Version부터는 도커 이미지를 만들어 주지 않는다고 합니다. 그래서 시놀로지 도커의 레지스트리(=도커 허브)에서 STRAPI를 받아서 사용할 겨우 구 버전(STRAPI V3)의 STRAPI만 설치할 수 있습니다.
그래서 직접 도커 이미지를 만들어서 시놀로지에 올려서 서비스해 보기로 했습니다.
도커이미지를 만드느라 3일 동안 삽질하면서 알게 된 정보를 공유합니다. 참고로 저는 도커에 대해서 많이 알지 못하는 초보입니다. 구글링을 하면서 해결한 내용이라 완전히 이해하면서 기록한 것은 아니므로 잘못된 내용이 있으면 지적질 부탁드립니다.
도커 이미지 만들기
먼저 도커가 설치되어 있다는 가정하에 진행하겠습니다. 윈도즈나 맥인 경우 아래의 도커 데스크톱을 설치하면 됩니다.
Dockerfile 작성하기
Docker Image를 만들기 위해서는 어떻게 만들 것인지에 대한 내용을 먼저 작성해야 하는데 그것이 Dockerfile 파일입니다. 여기에 작성된 내용 순서대로 이미지가 만들어집니다.
######################## ## Dockerfile 파일 ## ######################## # BASE OS # 이미지의 기본 OS를 무엇으로 할 것인가로 작성합니다. # 여기서는 어렵게 작성했지만.. 결론은 # node.js 14버전이 설치된 alpine 리눅스 OS를 기본 OS로 설치하고 포트는 1337로 설정합니다. ARG NODE_VERSION=14 FROM node:${NODE_VERSION}-alpine AS base-alpine EXPOSE 1337 FROM base-alpine # alpine 리눅스는 리눅스 중에서도 가장 가벼운 리눅스로 웬만한 기능들은 모두 제거한 OS입니다. # 그래서 기본적으로 사용할 최소한의 프로그램을 설치해 주는 것이 편리합니다. # apk update : 설치되어 있는 프로그램을 최신 버전으로 업데이트함 # apk add ~~ : ~~ 뒤에 작성된 프로그램들을 새로 설치함 RUN apk update && apk add build-base gcc autoconf automake zlib-dev libpng-dev nasm bash vips-dev # ARG는 Dockerfile 내에서만 사용되는 변수 # 설치할 STRAPI_VERSION을 작성합니다. latest라면 현재 시점에서 최신 버전을 설치하겠다는 의미입니다. ARG STRAPI_VERSION=latest # 먼저 STRAPI의 지정된 버전을 global로 alpine 리눅스에 설치합니다. # 이렇게 하면 굳이 로컬 컴퓨터에 STRAPI를 설치할 필요가 없습니다. RUN yarn global add @strapi/strapi@${STRAPI_VERSION} # /srv/app 디렉터리에 STRAPI 소스를 설치할 것입니다. # /srv/app 디렉터리를 만들고 첫 번째 사용자의 권한을 부여해서 디렉터리를 프리하게 만들어 줍니다. # 이제 이 폴더는 제 겁니다. RUN mkdir -p /srv/app && chown 1000:1000 -R /srv/app # 작업 폴더를 설정합니다. 이제 이 폴더에 프로젝트 파일들이 저장되고 실행될 것입니다. WORKDIR /srv/app # 로컬 컴퓨터와 데이터를 주고받을 볼륨을 설정합니다. 이제 이 폴더의 내용이 로컬 폴더가 똑같이 공유됩니다. VOLUME /srv/app # docker-entrypoint.sh를 실행시키기 위해서 docker-entrypoint.sh를 도커의 /usr/local/bin/으로 복사합니다. # 이 파일에 작성된 내용들이 실행되면서 프로젝트 파일이 만들어질 것입니다. COPY docker-entrypoint.sh /usr/local/bin/ # docker-entrypoint.sh의 권한을 풀어줍니다. # 파일이나 폴더를 생성할 때 문제가 생기지 않도록 권한을 부여합니다. RUN chmod 777 /usr/local/bin/docker-entrypoint.sh && ln -s /usr/local/bin/docker-entrypoint.sh # 도커 이미지를 통해 컨테이너를 처음 실행시킬 때 옮긴 docker-entrypoint.sh 파일을 실행시킵니다. ENTRYPOINT ["docker-entrypoint.sh"] # 도커 이미지를 통해 컨테이너를 처음 실행시킬 때 명령어가 실행됩니다. CMD ["strapi"]
ENTRYPOINT에 의해서 아래의 docker-entrypoint.sh 파일이 실행됩니다. 이 파일도 Dockerfile 파일과 동일한 공간에 위치해 있어야 합니다.
################################# ## docker-entrypoint.sh 파일 ## ################################# #!/bin/sh set -ea if [ "$*" = "strapi" ]; then if [ ! -f "package.json" ]; then DATABASE_CLIENT=${DATABASE_CLIENT:-sqlite} EXTRA_ARGS=${EXTRA_ARGS} echo "Strapi $(strapi version) 버전을 사용 중입니다." echo "현재 /srv/app 폴더에 프로젝트가 없습니다. 새로운 Strapi 프로젝트를 만듭니다..." DOCKER=true strapi new . --no-run \ --dbclient=$DATABASE_CLIENT \ --dbhost=$DATABASE_HOST \ --dbport=$DATABASE_PORT \ --dbname=$DATABASE_NAME \ --dbusername=$DATABASE_USERNAME \ --dbpassword=$DATABASE_PASSWORD \ --dbssl=$DATABASE_SSL \ $EXTRA_ARGS elif [ ! -d "node_modules" ] || [ ! "$(ls -qAL node_modules 2>/dev/null)" ]; then if [ -f "yarn.lock" ]; then echo "Node modules이 설치되어 있지 않습니다. Yarn을 이용하여 모듈을 설치 중입니다..." yarn install --prod --silent yarn build else echo "Node modules이 설치되어 있지 않습니다. npm을 이용하여 모듈을 설치 중입니다..." npm install --only=prod --silent npm run build fi fi if [ "$NODE_ENV" = "production" ]; then STRAPI_MODE="start" elif [ "$NODE_ENV" = "development" ]; then STRAPI_MODE="develop" fi echo "App을 시작합니다. (with ${STRAPI_MODE:-develop})..." exec strapi "${STRAPI_MODE:-develop}" else exec "$@" fi
Docker Image 만들기
자.. 이제 모든 준비는 끝났습니다. 이제 먼저 도커 이미지를 만들어 보겠습니다. 도커 이미지를 만드는 명령어는 아래와 같습니다. 여러 가지 다양한 옵션이 있지만 저는 아래 옵션만 적용했습니다.
docker build -t <생성할 이미지명>:<태그명> <Dockerfile 위치>
# 샘플 docker build -t njo2-strapi-4.5.3:1.0 .
가장 뒤에 있는 .
을 빼먹지 말고 붙여주세요. Dockerfile이 있는 위치를 지정하면 되는데 보통은 Dockerfile이 있는 위치에서 명령어를 실행시키기 때문에 현재 위치를 나타내는 .
을 붙여줍니다.
이 명령을 실행시키면 아무 변화가 없어 보입니다. 별도의 파일로 다운로드 되는 것은 아닙니다. 도커 명령어나 도커 데스크톱에서 Docker의 이미지 목록을 보면 결과물이 생성된 것을 볼 수 있습니다.
- 도커 데스크톱에서는 좌측 이미지 메뉴에서, CMD 창에서
docker image ls
명령어를 실행하면... - 현재 이미지의 리스트가 나옵니다.
Docker Image를 로컬 파일로 내려받기
시놀로지 Docker에 이미지를 추가하기 위해서는 먼저 도커 이미지를 내려받아야 합니다. 내려받는 파일은 압축파일인 .tar
로 다운로드 됩니다. 아래의 명령어를 입력하면 njo2-strapi-4.5.3.tar라는 tar 압축파일이 생성됩니다.
docker save -o <다운로드할 도커 이미지 압축 파일 이름> <생성할 도커 이미지>
# 샘플 docker save -o njo2-strapi-4.5.3.tar njo2-strapi-4.5.3
이제 이 압축파일을 그대로 시놀로지에 업로드합니다.
시놀로지 NAS에 도커이미지 적용하기
이렇게 생성된 이미지는 먼저 로컬 컴퓨터의 도커 데스크톱으로 정상적으로 컨테이너가 생성하고 실행되는지 확인하는 것이 좋습니다. 정상적인 이미지라고 판단되면 이제 시놀로지 NAS의 공유 폴더에 도커 이미지를 올립니다.
시놀로지에 이미지 올리기
위치는 어디든지 상관없습니다. 저는 _IMAGES
라는 폴더를 만들고 올렸습니다.
시놀로지 도커에 이미지 등록하기
이제 이 이미지를 시놀로지 도커에 등록을 합니다. 시놀로지에서 도커 프로그램을 실행시킵니다.
- 도커 앱에서 좌측 메뉴 중 이미지를 선택합니다.
- 상단 메뉴 중 추가를 선택합니다.
- 파일에서 추가를 선택합니다.
업로드한 폴더에서 이미지 파일을 선택해서 이미지를 추가합니다. 1GB가 넘어가는 크기라 추가하는데 시간이 좀 걸립니다.(대략 2~5분 정도?) 추가에 실패하는 경우가 있는데 여러 번 더 시도해 보면 올라갈 수 있습니다.
그러면 위와 같이 이미지가 등록됩니다.
Strapi Docker 컨테이너 실행하기
이제 올린 이미지로 컨테이너를 실행하면 되는데, 그전에 먼저 DB도 설치를 해야 합니다. 만약 별도의 DB를 설치하지 않는다면 내장되어 있는 경량의 sqlite를 자동으로 사용하게 되는데 강력한 DB는 아닙니다. 그래서 가능하면 postgreSQL를 설치하고 사용하는 것이 좋습니다.
설치하는 방법은 아래 포스팅을 참조하세요.
위의 포스팅에는 Strapi 컨테이너를 실행하는 부분도 있긴 한데, 아래의 내용이 개선된 내용이니 아래를 참조하시는 편이 좋습니다.
위의 이미지에서 추가한 Strapi 이미지를 더블 클릭해서 컨테이너 설치를 시작합니다.
먼저 네트워크를 선택합니다. 네트워크는 postgreSQL와 동일한 네트워크를 선택하면 됩니다.
- 보통 bridge를 선택하시면 됩니다.
- 다음 버튼을 클릭합니다.
- 컨테이너 이름을 먼저 설정합니다.
- 고급 설정을 선택합니다.
- 환경 탭을 선택하고 환경 변수를 입력합니다.
- 추가 버튼을 7번 클릭해서 입력칸 7개를 만듭니다.
- 기본 7개의 환경 변수 정보를 입력합니다.
설정해야 하는 환경정보는 아래와 같습니다. 7개의 중요 변수와 4개의 추가 변수를 입력합니다.
설정값 정보 환경 변수 설정
환경 변수 | 값 | 설명 |
---|---|---|
NODE_ENV | development | development / production 개발을 할 것인지. |
DATABASE_CLIENT | postgres | 사용할 db 종류를 작성합니다. |
DATABASE_NAME | strapi | strapi_postgres에서 설정한 DB 이름을 작성합니다. |
DATABASE_HOST | 192.168.0.10 | 시놀로지 NAS의 아이피 주소를 작성합니다. |
DATABASE_PORT | 15432 | strapi_postgres에서 설정한 Port 번호를 작성합니다. |
DATABASE_USERNAME | strapi | strapi_postgres에서 설정한 User를 작성합니다. |
DATABASE_PASSWORD | strapi | strapi_postgres에서 설정한 비밀번호를 작성합니다. |
아래는 옵션
환경 변수 | 값 | 설명 |
---|---|---|
DATABASE_SSL | SSL 보안 여부를 작성합니다. | |
JWT_SECRET | 사용자 권한 플러그 인용 JWT에 서명하는 데 사용되는 암호입니다. | |
ADMIN_JWT_SECRET | 관리자 패널용 JWT 서명에 사용되는 암호입니다. | |
APP_KEYS | 세션 쿠키에 서명하는 데 사용되는 비밀 키입니다. |
- 링크 탭을 클릭합니다.
- 추가 버튼을 클릭합니다.
- 컨테이너 이름 중에 postgreSQL를 선택합니다. 미리 postgreSQL를 설치해 놔야 목록에 나옵니다.
- 임의의 별칭을 입력합니다.
이제 고급 설정을 빠져나와서 다음 버튼을 클릭합니다.
다음으로 포트 설정을 합니다.
- 로컬 포트는 실제로 서비스할 포트를 작성하면 됩니다. 해당 포트는 방화벽에서 열어줘야 하며 해당 포트로 접속을 할 수 있습니다. 임의의 번호를 입력하면 됩니다.
- 컨테이너 포트를 작성해야 하며 이포트번호는 특별한 경우가 아니라면 1337포트를 사용하면 됩니다. (dockerFile 설정에서
EXPOSE 1337
와 동일하면 됨 )
마지막으로 볼륨을 설정하면 됩니다. 이 볼륨은 로컬 컴퓨터(시놀로지에서는 공유 폴더)의 폴더와 도커 이미지의 특정 폴더와 1:1 매칭이 됩니다. 그래서 이곳에 파일을 추가하게 되면 다른 쪽 폴더에도 동일하게 동기화가 됩니다.
- 파일 추가 또는 폴더 추가 버튼을 클릭합니다. (Strapi는 폴더만 추가하면 됨)
- 파일/폴더에는 시놀로지 나스의 원하시는 특정 폴더를 선택해 주면 됩니다.
- 마운트 경로는 도커 이미지 경로를 작성하면 되는데 특별한 경우가 아니라면 /srv/app를 작성하면 됩니다. (dockerFile 설정에서
VOLUME /srv/app
와 동일하면 됨 )
이제 최종적으로 이렇게 생성될 것이라는 요약정보를 보여줍니다.
- 완료 버튼을 클릭하면 이제 컨테이너가 생성되고 바로 실행이 됩니다.
방화벽 열어주기
설치가 완료되었다면 시놀로지 NAS에서 방화벽을 열어줘야 합니다. 열어주지 않으면 초기 설치될 때 컨테이너가 바로 죽어버릴 수 있습니다.
방화벽은 제어판의 보안 탭에서 지정합니다.
- 제어판 아이콘 선택합니다.
- 보안 탭을 선택합니다.
- 방화벽 탭을 선택합니다.
- 규칙 편집 버튼을 클릭합니다.
위의 3개의 Strapi 관련 프로그램에 대해서 방화벽을 해제해 주면 됩니다. (프로그램명은 다를 수 있습니다.)
접속해 보기
접속해서 위와 같은 화면이 나타나면 일단 90%는 설치가 완성되었다고 보시면 됩니다. 나머지 문제는 아래 오류에 대해서 처리하면 해결될 수 있습니다.
발생할 수 있는 오류
초기 컨테이너 실행 오류
상황
위의 세팅을 하고 처음 컨테이너를 실행하면 컨테이너가 바로 죽어버리는 현상이 발생할 수 있습니다.
그 이유는 처음 실행될 때 package.json에 의해 node_modules 폴더에 의존성 파일이 설치되게 되는데 이게 시놀로지의 방화벽에 의해 막혀서 설치가 되지 않아 오류가 발생하는 현상입니다.
해결 방법
해결 방법은 간단합니다. 초기 설치할 때 방화벽을 풀어주고 설치하면 설치가 잘 진행됩니다. 다만 설치가 끝나면 다시 방화벽을 활성화시켜야 합니다. 추후 플러그인을 설치하려면 그때도 다시 방화벽을 열었다가 설치가 완료되면 다시 닫아주셔야 합니다. (위의 설명 확인해 주세요.)
xxx파일 : no such file or directory
docker-entrypoint.sh : no such file or directory 또는 package.json : no such file or directory
상황
Dockerfile 을 만들어 컨테이너로 실행할 때 위와 같은 식의 오류가 발생하는 경우가 있습니다. 아무리 살펴봐도 잘못된 부분이 없는데 위의 오류가 발생합니다. 저는 이것 때문에 거의 2일을 날려버렸습니다.
여러 다른 이유가 있겠지만 저의 문제는 CRLF
문제였습니다. CRLF
, LF
는 줄바꿈 타입인데 CRLF
는 (\r\n), LF
는 **(\n)**의 보이지 않는 코드가 들어갑니다. 이것은 리눅스, 윈도우 같은 OS 별로 다른 형태의 기본 줄바꿈을 가지고 있기 때문에 발생하는 문제입니다.
해결 방법
해결 방법은 CRLF
였다면 LF
로 변경해 보고 LF
였다면 CRLF
로 줄바꿈 형식을 바꿔보시면 됩니다.
만약 vsCode를 사용한다면 아래와 같이 수정이 가능합니다.
- 우측 하단의
CRLF
또는LF
를 클릭합니다. - 상단 중앙에 2개 중 선택하는 메뉴 중에 하나를 선택합니다.
Strapi: Warning an error occurred while requesting the API
상황
이 부분은 정상적으로 설치가 되고 초기 화면까지는 나오는데 실제 관리자 화면(https://localhost/admin)으로 접속했을 때 Strapi: Warning an error occurred while requesting the API
라는 오류 화면이 나올 때가 있습니다.
해결 방법
위의 문제는 보통 서비스하기 전에 먼저 Build를 한번 해 놓으면 해결이 됩니다. ( yarn build )
위의 코드에서는 해결 방법을 이미 적용시켜놓은 상태인데 덕분에 처음 로딩 시 build 하는 시간이 더 소요가 됩니다.
# ... if [ -f "yarn.lock" ]; then echo "Node modules이 설치되어 있지 않습니다. Yarn을 이용하여 모듈을 설치 중입니다..." yarn install --prod --silent yarn build # 빌드를 추가함 else echo "Node modules이 설치되어 있지 않습니다. npm을 이용하여 모듈을 설치 중입니다..." npm install --only=prod --silent npm run build # 빌드를 추가함 fi # ...
oci runtime exec failed "/bin/bash"
상황
위의 문제는 사실 오류는 아닙니다. 다만 해당 OS에 bash가 없어서 생기는 문제입니다.
도커 이미지의 베이스 OS로 사용하는 alpine리눅스가 몸집을 가볍게 하기 위해 대부분의 기능을 제거한 버전이라 용량이 작은 대신 없는 기능들이 있기 때문입니다. 그중에 bash 프로그램도 뺏기 때문에 이런 현상이 생기는 것입니다. bash 대신 sh는 기본으로 사용이 가능합니다.
해결 방법
위의 문제는 dockerFile에서 alpine를 설치하는 코드 다음에 아래의 코드를 추가하면 됩니다.
RUN apk update && apk add bash
위의 dockerFile에는 적용해 놓은 상태입니다.