[배포] dev 서버 변경 사항 정리

김태훈·2023년 9월 23일
0
post-thumbnail

https://velog.io/@goat_hoon/%EB%B0%B0%ED%8F%AC-dev-%EC%84%9C%EB%B2%84-CICD-%EC%A0%95%EB%A6%AC
이전에 배포했던 상황에서 변경사항이 있어서 다시 정리하게 되었다.

이전의 dev환경의 문제는 쓸 데 없이 ec2가 세개가 띄워져 있다는 점이었다. 사실 개발환경을 위한 서버임에도 ec2가 세개가 띄워져 있다는 것은 자원의 낭비가 분명했다.

그래서 ec2를 한개로 줄이기 위한 개편을 했다.

CI/CD를 위한 github action yml차이의 변화를 보면서 설명하겠다.

이전 버전

  - name: Deploy to dev
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.HOST_DEV }}
        username: ec2-user
        key: ${{ secrets.PRIVATE_KEY }}
        script: |
            ssh spring sudo docker rm -f $(ssh spring sudo docker ps -qa)
            ssh spring sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKER_DEV_REPO }}
            ssh spring sudo docker run -dp 8080:8080 ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKER_DEV_REPO }}

이 전 배포상황의 포스트를 보면 알겠지만 public으로 열려있는 nginx용 ec2를 활용하여 next가 빌드된 ec2, spring boot가 빌드된 ec2로 프록시역할을 해주고 있었다.
하지만 dev환경에서도 ec2를 세대를 띄워야만 할까? 이것은 낭비이다.
ec2 하나만으로도 nginx용 docker container를 거쳐서 포트매핑으로 next 컨테이너, spring boot 컨테이너와 통신할 수 있게 하는 것이 좋다.


개편 버전

   - name: Deploy to dev
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.HOST_DEV }}
        username: ec2-user
        key: ${{ secrets.PRIVATE_KEY }}
        script: |
            sudo docker stop backend
            sudo docker rm backend
            sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKER_DEV_REPO }} 
            sudo docker run -dp 8080:8080 --name=backend ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKER_DEV_REPO }}

ssh 로 외부 서버로 접속하는 것이 사라졌다.
실제 서버로 들어가서 확인해보자.

위 사진은 dev용 ec2에 띄워진 docker container들이다.
외부에서는 443포트로 들어와서 80으로 redirect시킨 후 해당 container들 중, nginx로 요청이 들어올 것이다.
그렇다면 nginx config파일이 어떻게 되어있는지 nginx 컨테이너로 들어가 확인하자.

- nginx 설정

user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    server_tokens off;

    add_header X-XSS-Protection "1; mode=block";
    add_header Strict-Transport-Security    "max-age=63072000; includeSubdomains; preload";
    add_header X-Content-Type-Options   nosniff;
    add_header X-Frame-Options SAMEORIGIN;

    client_body_timeout 60;
    client_header_timeout 60;
    keepalive_timeout 90;
    send_timeout 120;

    ssl_prefer_server_ciphers on;

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    include /etc/nginx/blockuseragents.rules;
    server {
        listen 80;
        server_name localhost;

        location / {
            if ($request_method !~ ^(GET|HEAD|POST|OPTION)$) {
                return 444;
            }
            proxy_pass         http://172.17.0.1:3000;
            proxy_http_version 1.1;
        }
        location /api {
            proxy_pass         http://172.17.0.1:8080;
            proxy_http_version 1.1;
        }
    }
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    include /etc/nginx/conf.d/*.conf;

proxy_pass 경로가 이제는 172.17.0.1에, port 번호로 구분이 되어있다.
이렇게 되면 nginx의 요청이 해당 포트에 따라 프록시되면서, next,와 inforum-dev 컨테이너에 도달할 것이다.

그렇다면 172.17.0.1는 무슨 private ip일까?
이는 도커의 네트워크 지식과 관련이 있다.

- 도커 네트워크

docker 컨테이너를 띄운 ec2 내에서 ifconfig 를 실행하면

docker0 의 bridge를 볼 수 있다.
해당 인터페이스의 용도는 이따가 설명하겠다.

또한 veth 네트워크 인터페이스도 생긴다.

도커는 각 컨테이너에 외부와의 네트워크를 제공하기 위해 컨테이너마다 가상의 네트워크 인터페이스를 생성하는데 이러한 네트워크 인터페이스의 이름이 veth로 시작하는 인터페이스이다.

현재 ec2내에서는 세개의 docker container가 돌고 있기 때문에 3개의 veth 네트워크 인터페이스가 생성되어있음을 알 수 있다.

이 때, docker0 bridge는 각 veth 인터페이스와 바인딩되어서 호스트의 enX0(eth0) 즉, 외부 네트워크와 연결이 되어진다. 즉 docker0는 컨테이너끼리 통신하기 위한 네트워크 브리지인 것이다.

그러면 직접 bridge 네트워크를 살펴보자.
docker network inspect bridge
를 보면

[
    {
        "Name": "bridge",
        "Id": "4dcfb0a303d3ab6cd12bd137a69feb56d0c902eccfaafee3ad33ea80e8731b96",
        "Created": "2023-09-12T05:17:28.658823535Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "551fe367644ee8cacdc8fbc2ec505a7c7a99dce0e8f92e62268f786b73e3c63d": {
                "Name": "frontend",
                "EndpointID": "6b21d79f926d166feded985964fafb87ae47b92e017d5a9efe91c17792d6bb30",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "61ad40c93ab6038b38f70255f1343153cb73d2a03dff6cfa44a5b2bb847ee990": {
                "Name": "backend",
                "EndpointID": "8afa56983b5e765e5a811e513dd9b1a1dd15b8a099bb96d3d630658beb846ae2",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            },
            "e52074e868cd6f9e84950d7eab67a7d12c640046ed0adb340aafc8038df193fe": {
                "Name": "nginx",
                "EndpointID": "c2c1d6c91317d174b63d7f92083be48682e2b157fc5cc50b5d4edaa66326e63b",
                "MacAddress": "02:42:ac:11:00:04",
                "IPv4Address": "172.17.0.4/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

이렇게 bridge 네트워크에 세가지 컨테이너가 물려있음을 알 수 있다.
근데 172.17.0.1로 연결이 되어있어야 docker0 bridge 네트워크로 컨테이너끼리 통신할 수 있는 것이 아닌가?
bridge network에는 gateway가 기본적으로 명시되어 있지 않았다.
172.17.0.1:3000 과 172.17.0.1:8080으로 리버스 프록시 해주기 위해서는 한가지 설정이 더 필요해보였다.
하지만, default로 모든 컨테이너의 routing table이 172.17.0.1과 주고 받을 수 있게 설정이 되어있다.

이를 확인하기 위해서는
각 컨테이너에 직접 들어가 route정보를 치면 된다.
docker exec {container id} route

기본적으로 gateway가 172.17.0.1 (docker0 ip address)로 설정되어 있음을 확인할 수 있다.

다음은 iptables -S
명령어로 살펴본 ip 규칙들이다.

이를 통하여, ec2하나에서 docker0 bridge interface를 활용한 docker container들끼리의 통신으로 ec2 하나에서 dev환경을 구축할 수 있었다.

profile
기록하고, 공유합시다

0개의 댓글