Nginx Reverse Proxy Example with Node.js Express

   - reverse proxy 관련 도커 이미지는 jwilder/nginx-proxy 와 JrCs/docker-letsencrypt-nginx-proxy-companion 를 사용했습니다.
   - https://github.com/jwilder/nginx-proxy
   - https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion

  • https://github.com/jmyoon4488/docker-nginx-reverse-proxy
  • docker-compose 사용
  1. nginx-proxy 컨테이너 실행
  2. Dockerfile 로 Node.js 이미지 파일 빌드
  3. 빌드한 이미지로 Node.js 웹서버 실행
  4. 잘 적용되었는지 확인!

  1. nginx-proxy container 실행해줍니다.

    • /your/path/nginx-proxy - 해당 부분은 자신의 서버에 맞는 경로로 바꿔주세요.
    • proxy/nginx-proxy-letsencrypt.yml

    version: '3' services: nginx-proxy: container_name: nginx-proxy image: jwilder/nginx-proxy ports: - 80:80 - 443:443 restart: always volumes: - /your/path/nginx-proxy/log:/var/log/nginx  - /your/path/nginx-proxy/html:/usr/share/nginx/html - /your/path/nginx-proxy/certs:/etc/nginx/certs         - /your/path/nginx-proxy/vhost.d:/etc/nginx/vhost.d - /your/path/nginx-proxy/config:/etc/nginx/conf.d - /var/run/docker.sock:/tmp/docker.sock:ro labels: - com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy etsencrypt-nginx-proxy: container_name: leten-nginx-proxy image: jrcs/letsencrypt-nginx-proxy-companion restart: always depends_on: - nginx-proxy volumes: - /your/path/nginx-proxy/certs:/etc/nginx/certs - /your/path/nginx-proxy/vhost.d:/etc/nginx/vhost.d - /your/path/nginx-proxy/html:/usr/share/nginx/html - /var/run/docker.sock:/var/run/docker.sock:ro networks: default: external: name: nginx-proxy

    $ docker-compose -f nginx-proxy-letsencrypt.yml up -d
    
    • $ docker ps 명령어로 컨테이너가 정상적으로 실행되고 있는지 확인.
  2. Dockerfile 로 Node.js 이미지 빌드

    • node/Dockerfile
    FROM node:carbon
    
    ENV SRCDIR /src
    RUN mkdir -p $SRCDIR/app && chown -R node:node $SRCDIR
    
    WORKDIR $SRCDIR
    COPY ./package.json $SRCDIR
    
    RUN npm install
    
    COPY . $SRCDIR
    
    EXPOSE 3000
    WORKDIR $SRCDIR/app
    
    CMD ["node", "app.js"]
    • package.json
    {
        "name": "node",
        "version": "0.0.0",
        "private": true,
        "scripts": {
            "start": "node ./app/app.js"
        },
        "dependencies": {
            "debug": "~2.6.9",
            "ejs": "~2.5.7",
            "express": "~4.16.0",
            "request": "^2.88.0",
            "uuid": "^2.0.2"
        }
    }
    • build image
    $ docker build --tag nodejs:test .
    
    • $ docker images  로  이미지 확인
    REPOSITORY                               TAG                 IMAGE ID            CREATED             SIZE
    nodejs                                   test                000000000000        24 minutes ago      902 MB
    
  3. 빌드한 이미지로 Node.js 컨테이너 실행

    • 4개 항목은 꼭 자신에게 맞는 값으로 변경 - VIRTUAL_HOST, VIRTUAL_PORT, LETSENCRYPT_HOST, LETSCRYPT_EMAIL
    • 포트를 변경하려면 Dockerfile 에서 EXPOSE 포트를 변경하고 다시 빌드해 줍니다.
    • node/node-test.yml
    version: '3'
    
    services:
      nodejs-test:
        container_name: node-test
        image: nodejs:test
        volumes:
          - ./app:/src/app
        environment:
          - VIRTUAL_HOST=sub.domain.com
          - VIRTUAL_PORT=3000
          - LETSENCRYPT_HOST=sub.domain.com
          - LETSCRYPT_EMAIL=your@email.com
    
    networks:
      default:
        external:
          name: nginx-proxy
    • 컨테이너 확인
    $ docker ps
    
  4. 프록시 및 letsencrypt 적용 확인

    • 브라우저를 통해 설정에 적용한 서브도메인으로 접속해봅니다.
    • nginx 설정파일을 확인해 봅니다.
    $ docker exec nginx-proxy cat /etc/nginx/conf.d/default.conf
    

    or

    $ sudo cat ./config/default.conf
    
    • default.conf
    ~~~~~~~
    # sub.domain.com
    upstream sub.domain.com {
                    ## Can be connected with "nginx-proxy" network
                # node-test
                server 172.5.0.1:3000;
    }
    server {
        server_name sub.domain.com;
        listen 80 ;
        access_log /var/log/nginx/access.log vhost;
        return 301 https://$host$request_uri;
    }
    server {
        server_name sub.domain.com;
        listen 443 ssl http2 ;
        access_log /var/log/nginx/access.log vhost;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers 'CODE~~~~~~~~~~~~~';
        ssl_prefer_server_ciphers on;
        ~~~ many options 
        include /etc/nginx/vhost.d/default;
        location / {
            proxy_pass http://sub.domain.com;
        }
    }
  5. 만약 다른 웹서버 컨테이너를 추가하고 싶다면...

    • 추가할 웹서버는 도커를 이용해 실행해야 합니다.
    • 컨테이너는 반드시 프록시 컨테이너와 같은 도커 네트워크 상에 위치해야 합니다. (nginx-proxy)
    • 위에서 지정한 4가지 옵션을 꼭 입력해 주세요.


[Nginx-proxy 이미지 사용]

- docker-compose 를 통한 컨테이너 구축


- 서브도메인을 분기 시켜줄 프록시 컨테이너

* nginx-proxy.yaml


version: '2'
services:
nginx-proxy:
container_name: nginx-proxy
image: jwilder/nginx-proxy
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- /home/test/nginx-proxy/log:/var/log/nginx

networks:
default:
external:
name: nginx-proxy



- 기본 웹서버 컨테이너

* nginx-www.yaml


version: '2'
services:
nginx-www:
image: nginx:latest
environment:
- VIRTUAL_HOST=www.domain.com
container_name: nginx-www

networks:
default:
external:
name: nginx-proxy



- 서브도메인을 테스트 할 test 컨테이너 (test 대신 다른 웹서버 이미지를 사용해서 테스트 해야합니다.)

* test.yaml


version: '2'
services:
test-container:
image: test
container_name: test
environment:
- VIRTUAL_HOST=sub.domain.com
- VIRTUAL_PORT=3000

networks:
default:
external:
name: nginx-proxy



- 프록시 컨테이너 내 nginx 설정 파일 확인

/etc/nginx/conf.d/default.conf

~~~
server {
    server_name _; # This is just an invalid value which will never trigger on a real hostname.
    listen 80;
    access_log /var/log/nginx/access.log vhost;
    return 503;
version: '2'
}
# www.domain.com
upstream www.domain.com {
                ## Can be connected with "nginx-proxy" network
            # nginx-www
            server 192.0.0.1:80;
}
server {
    server_name www.domain.com;
    listen 80 ;
    access_log /var/log/nginx/access.log vhost;
    location / {
        proxy_pass http://www.domain.com;
    }
}
# test.domain.com
upstream test.domain.com {
            ## Can be connected with "nginx-proxy" network
            # test web server container
            server 192.0.0.2:3000;
}
server {
    server_name test.domain.com;
    listen 80 ;
    access_log /var/log/nginx/access.log vhost;
    location / {
        proxy_pass http://test.domain.com;
    }
}



---------------

[CentOS 7]

- 도커 유용할 것 같은 내용 정리 중


# 컨테이너 로그 필터링

# docker logs 컨테이너 이름 2>&1 | grep "필터링 할 내용"


# 컨테이너 사용량 모니터링

# docker stats --format "table {{.Name}}\t{{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"

# repository

sudo yum -y update

sudo yum -y install epel-release


# curl, git, wget, python2.7

#sudo yum -y install curl git

sudo yum -y install wget


# docker

sudo yum -y install yum-utils device-mapper-persistent-data lvm2

sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

sudo yum makecache fast

sudo yum -y install docker-ce


# docker-compose

sudo yum -y install python-pip

sudo pip install docker-compose

 

# docker-compose

#curl -L https://github.com/docker/compose/releases/download/1.14.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

#chmod +x /usr/local/bin/docker-compose


# docker

systemctl start docker

systemctl enable docker

[도커 컨테이너간 네트워크를 잘 몰라서 생긴 삽질]


- 도커를 사용해서 레드마인 서버를 구축하면서 겪은 상황입니다.


- 아래와 같이 docker-compose 를 이용해 레드마인과 mariadb 를 구축했습니다.


* DB 포트인 3306 과 웹포트인 80, 8080 은 이미 사용중이어서 다른 포트로 매핑했습니다.

  여기서... 오해의 시작이...



version: '3'

services:
redmine_db:
image: mariadb
container_name: redmine_db
restart: always
volumes:
- local_directory/db:/var/lib/mysql
ports:
- 9096:3306
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: redmine
MYSQL_USER: user
MYSQL_PASSWORD: user_password
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
networks:
- default
- redmine_network

redmine_web:
image: redmine
container_name: redmine_web
restart: always
volumes:
- local_directory/themes:/usr/src/redmine/public/themes
- local_directory/plugins:/usr/src/redmine/plugins
- local_directory/files:/usr/src/redmine/files
ports:
- 9090:3000
links:
- redmine_db:mysql
environment:
REDMINE_DB_PORT: 9096
REDMINE_DB_MYSQL: redmine_db
REDMINE_DB_USERNAME: user
REDMINE_DB_PASSWORD: user_password
REDMINE_DB_DATABASE: redmine
REDMINE_DB_ENCODING: utf8
depends_on:
- redmine_db
networks:
- redmine_network

networks:
redmine_network:


- 위와 같이 구성한 뒤 컨테이너를 올렸는데 redmine_web 에서 계속 connection 관련 오류가 납니다.


- 도대체 뭘 잘못한거지... 계정도 제대로 만들어졌고 Heidisql 을 이용해서 host:9096 으로 접속도 제대로 되는걸 확인했지만


  레드마인은 계속 오류로 인해 재시작만 반복하네요.



[결국 원인을 찾음]


* 문제는 바로 이부분..



environment:
REDMINE_DB_PORT: 9096 // DB 컨테이너 3306 포트와 매핑된 호스트 서버 포트 / 틀렸다.. 3306 이 정답.
REDMINE_DB_MYSQL: redmine_db // DB 컨테이너 서비스명


- YAML 파일에 작성되는 항목들은 서비스 이름으로 컨테이너들간 연결이 가능하기 때문에 redmine_web 컨테이너에서


  redmine_db 컨테이너로 접근할 시 외부 호스트 포트 인 9096 포트 으로 접근할게 아니라 기본 3306 포트로 접속해야 됩니다.


 9096 포트는 아예 호스트 서버 외부에서 redmine_db 에 직접 접근할 때 사용됩니다.


- 이전 포스트에서는 기본 포트인 3306, 80, 8080 을 사용해서 몰랐는데.. 이걸 몰라서 이번에 삽질을 좀....






[docker 를 이용해 mysql + redmine 설치]


* docker-compose 사용


(docker-compose-redmine.yaml)



version: '2.1'

services:
redmine:
image: redmine
restart: always
container_name: redmine
ports:
- 3000:3000
environment:
REDMINE_DB_MYSQL: db
REDMINE_DB_PASSWORD: pass
REDMINE_DB_DATABASE: redmine
REDMINE_DB_ENCODING: utf8
depends_on:
db:
condition: service_healthy

db:
image: mysql
restart: always
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: pass
MYSQL_DATABASE: redmine
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci



$ docker-compose -f docker-compose-redmine.yaml up -d


위 상태로 실행하면 redmine 이 실행되었다가 계속 restart 를 반복합니다.


mysql 은 잘 실행되지만 redmine 이 실행 도중 오류가 발생하는 것으로 보이는데..


$ docker logs -f redmine


redmine 로그를 확인해보면 


' Authentication plugin 'caching_sha2_password' cannot be loaded '


위와 같은 오류를 확인할 수 있습니다.


검색 해 본 결과 mysql 버전에 따른 오류라고 하는데요...

(https://stackoverflow.com/questions/49979089/authentication-plugin-caching-sha2-password-cannot-be-loaded-in-circleci-mysql)


그래서 mysql 버전을 5.7 버전으로 지정해서 설치하니까 잘 됩니다.


image: mysql    ->   image: mysql:5.7




version: '2.1'

services:
redmine:
image: redmine
restart: always
container_name: redmine
ports:
- 3000:3000
environment:
REDMINE_DB_MYSQL: db
REDMINE_DB_PASSWORD: pass
REDMINE_DB_DATABASE: redmine
REDMINE_DB_ENCODING: utf8
depends_on:
db:
condition: service_healthy

db:
image: mysql:5.7
restart: always
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: pass
MYSQL_DATABASE: redmine
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci




[추가사항]


혹시나 해서 mysql 대신 mariadb 로 하니까 최신버전이어도 잘 되는듯 합니다.


image: mariadb


(mariadb 만세)


+ Recent posts