IT's Jenna

배포 프로세스 정리 - 실무 본문

서버배포/docker

배포 프로세스 정리 - 실무

developer Jenna 2021. 7. 21. 09:37
내가 일하면서 보려고 만든 배포 프로세스 정리 

CI/CD란?

CI (Continuous Integration) : 애플리케이션의 새로운 코드 변경 사항이 정기적으로 빌드 및 테스트되어 공유 repository에 통합되는 것을 의미합니다.

CD (Continuous Delivery OR Continuous Deployment) : 공유 repository에 자동으로 release 하고, production level까지 자동으로 deploy 하는 것을 의미합니다.

 

즉, CI가 새로운 소스코드의 빌드, 테스트, 병합까지를 의미하고 CD는 개발자 환경을 넘어 고객용 production 환경까지 release되는것 이라고 생각할 수 있습니다.

CI/CD 프로세스

1. pre-setup.sh

#!/usr/bin/env bash

#update instance
# sudo apt-get update && sudo apt-get -y upgrade
sudo apt-get update

# install packages
sudo apt-get install docker.io -y


sudo usermod -a -G docker $USER
sudo curl -L https://github.com/docker/compose/releases/download/1.21.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# sudo chown -R $USER:$(id -gn $USER) /home/ubuntu/.config
sudo usermod -a -G docker $USER

# awscli install
sudo apt install awscli

# bcrypt pre install python
# sudo apt-get install -y build-essential python
        • 배포를 위해서 가장 먼저 설정되어야 하는 단계들 입니다.
        • 위 내용은 자동으로 실행되는 것이 아닌 개발자가 인스턴스에 접속 후에 직접 한 줄씩 실행해주어야 하는 코드의 모음입니다.
        • apt-get (Advanced Packageing Tool, APT) : 우분투를 포함한 리눅스 계열에서 사용하는 패키지 관리 툴입니다. 터미널을 통해서 간편하게 소프트웨어를 설치, 업데이트 및 제거하는 기능을 지원합니다.
        • sudo apt-get update : 운영체제에서 사용할 패키지들과 그 버전에 대한 정보를 업데이트하는 명령어입니다. 실제 패키지들을 각각 업데이트하는 것이 아닌 패키지 리스트 정보를 업데이트한다는 점에 유의하시기 바랍니다!
        • sudo apt-get install docker.io -y : 도커를 설치합니다. -y를 덧붙이면 설치 시 확인이 필요한 모든 사항에 yes가 자동으로 선택됩니다.
        • sudo usermod -a -G docker $USER : 여기서 $USER는 ubuntu입니다. -G로 docker라는 그룹을 생성하고 그 안에 ubuntu라는 계정을 생성합니다. 해당 계정이 sudo 권한을 가지도록 합니다.
        • curl (command line tool) : 명령줄에 URL을 이용해서 데이터 전송을 할 수 있도록 합니다.
        • sudo curl -L https://github.com/docker/compose/releases/download/1.21.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose : docker다운로드 URL을 전달해서 특정 경로에 docker를 다운로드합니다.
        • sudo chmod +x /usr/local/bin/docker-compose : docker가 설치된 경로에 권한을 줍니다.
        • 위 세줄은 동시에 실행 가능합니다. 
        • sudo apt install awscli : awscli를 설치합니다.
        • awscli (Amazon Command Line Interface) : awscli는 아마존 명령줄 인터페이스입니다. 명령줄 셀에서 명령을 사용하여 AWS와 상호작용할 수 있는 오픈소스 도구입니다. aws cli를 사용하면 터미널의 명령 프롬프트에서 브라우저 기반 AWS management console에서 제공하는 것과 동일한 기능을 구현 가능합니다. (AWS가 자체적으로 보유하고 있는 모듈입니다)
        • 다시 한번 강조하지만, 위 동작들은 모두 인스턴스 내에서 행해져야 합니다. 즉 인스턴스 내부에 도커 및 모듈들을 설치하는 과정입니다.

2. gitlab-ci.yml

variables:
  AWS_DEFAULT_REGION: ap-northeast-2
  IMAGE_NAME: ******
  REGISTRY_URL: ******
  DOCKER_HOST: tcp://docker:2375
  DOCKER_DRIVER: overlay2

stages:
  - push-image
  - deploy

  # cache:
  #   paths:
  #    - node_modules/

push-image:
  stage: push-image
  image:
    name: amazon/aws-cli
    entrypoint: ['']
  services:
    - docker:dind
  before_script:
    - amazon-linux-extras install docker
    - aws --version
    - docker --version
    - aws configure set region $AWS_DEFAULT_REGION
    - aws configure set aws_access_key_id $AWS_ACCESS_KEY
    - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
    - aws s3 cp s3://******/.env ./
    - aws configure set aws_access_key_id $EC2_AWS_ACCESS_KEY --profile ec2
    - aws configure set aws_secret_access_key $EC2_AWS_SECRET_ACCESS_KEY --profile ec2
  script:
    - docker build -t $IMAGE_NAME .
    - aws ecr get-login-password --profile ec2 | docker login --username AWS --password-stdin $REGISTRY_URL
    - docker tag $IMAGE_NAME:latest $REGISTRY_URL
    - docker push $REGISTRY_URL
  only:
    - master

deploy:
  image: docker:latest
  stage: deploy
  script:
    - fid=$(mktemp)
    - echo "$DEPLOY_KEY" > "$fid"
    - chmod 400 "$fid"
    - scp -v -o StrictHostKeyChecking=no -i "$fid" ./docker-compose.yml "$DEPLOY_USER@$DEPLOY_HOST":/home/ubuntu
    - scp -v -o StrictHostKeyChecking=no -i "$fid" ./scripts/deploy-stage.sh "$DEPLOY_USER@$DEPLOY_HOST":/home/ubuntu
    - scp -v -o StrictHostKeyChecking=no -i "$fid" ./scripts/custom.conf "$DEPLOY_USER@$DEPLOY_HOST":/home/ubuntu
    - ssh -o "StrictHostKeyChecking=no" -i "$fid" "$DEPLOY_USER@$DEPLOY_HOST" aws configure set aws_access_key_id $EC2_AWS_ACCESS_KEY
    - ssh -o "StrictHostKeyChecking=no" -i "$fid" "$DEPLOY_USER@$DEPLOY_HOST" aws configure set aws_secret_access_key $EC2_AWS_SECRET_ACCESS_KEY
    - ssh -o "StrictHostKeyChecking=no" -i "$fid" "$DEPLOY_USER@$DEPLOY_HOST" aws configure set region $AWS_DEFAULT_REGION
    - ssh -o "StrictHostKeyChecking=no" -i "$fid" "$DEPLOY_USER@$DEPLOY_HOST" sh deploy-stage.sh
    - rm -rf "$fid"
  only:
    - master
  • gitlab 내에서 자동으로 실행될 명령어 모음 스크립트입니다. 별표 표시는 보안상의 이유로 대체된 부분입니다.
  • variables: 배포에 필요한 ecr 관련 정보들을 선언해 둡니다. 아마존에서 생성한 ecr 이미지명 그리고 URL 정보를 입력합니다.
  • stages: 배포 실행 단계를 의미합니다. 같은 stage의 동작들은 병렬로 실행되고 각 stage 간에는 직렬로 실행됩니다.
  • push-image:
    • stage : push-image 단계에서 실행됩니다.
    • image : push 할 이미지명을 입력합니다. 위 코드에서는 aws-cli이미지를 push 합니다.
    • before_script : script 동작 실행 전에 설정되어야 할 부분을 입력합니다. 해당 정보는 이미지에 등록돼서 push 됩니다.
    • 미리 aws에서 생성해둔 s3 access key/secret key  그리고 ec2 access key/secret key를 설정합니다.
    • s3에 미리 .env 파일을 업로드해두고 해당 경로를 지정해두면 일일이 변수를 설정할 필요 없이 사용 가능합니다. 해당 파일은 docker가 빌드되는 시점에 불러와집니다. 위 코드에서 .env 파일에는 DB정보를 저장해서 업로드해두었습니다.
    • 해당 코드의 위쪽 variables에서 지정되지 않은 변수들은 gitlab의 settings - CI/CD - Variables에 미리 지정해 두었습니다. 아래 그림 참고 가능합니다.
    • script : 도커를 빌드합니다. aws에서 생성한 ecr 등록 URL에 빌드한 도커를 push 합니다.

<gitlab CI/CD Variables setting 예시>

    • deploy:
      • image : 배포할 이미지를 선택합니다. 위 코드에서 docker의 가장 최신 버전을 배포합니다.
      • echo "$DEPLOY_KEY" > "$fid" : pem키를 등록합니다. pem키 전체 내용을 gitlab Variables DEPLOY_KEY에 넣어줍니다.
      • chmod 400 "$fid" : 사용자만 키파일을 읽을 수 있도록 키페어에 권한을 설정합니다.
      • scp : 특정 파일들을 복사해서 ec2로 업로드 합니다.
      • ssh : push-image에서 ecr로 올린 이미지를 연결하기 위해 설정하는 단계입니다.
      • sh deploy-stage.sh : gitlab에서 가장 마지막으로 돌아가는 script입니다. 가장 최신 버전의 docker 이미지를 pull해서 사용합니다. 
      • rm -rf "$fid" : 키파일을 삭제합니다.

3. deploy-stage.sh

#!/bin/bash
BACKEND_URL=******:latest
# ADMIN_FRONTEND_URL=******:latest

aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin ${BACKEND_URL}
docker pull ${BACKEND_URL}
# docker pull ${ADMIN_FRONTEND_URL}

docker-compose -f docker-compose.yml -p server up -d --remove-orphans

docker rmi $(docker images --filter "dangling=true" -q --no-trunc)
    • gitlab에서 가장 마지막으로 돌아가는 script라고 언급했던 deploy-stage입니다.
    • URL : ecr의 가장 최신버전을 받아옵니다.
    • docker pull : 승인된 링크를 통해 docker 이미지를 pull 합니다.
    • docker-compose -f docker-compose.yml -p server up -d --remove-orphans : docker-compose를 실행합니다.
    • docker rmi $(docker images --filter "dangling=true" -q --no-trunc) : 사용하지 않는 이미지를 삭제합니다. 삭제할 이미지가 없는 경우 배포 과정에서 에러가 발생할 수 있지만 배포 과정에 문제 되지 않습니다.

4. docker-compose.yml

version: '3.6'
services:
  test_api:
    image: ******:latest
    restart: always
    environment:
      - VIRTUAL_HOST=******
      - VIRTUAL_PORT=2000
      - LETSENCRYPT_HOST=******
      - LETSENCRYPT_EMAIL=******
      - TZ=Asia/Seoul
      - NODE_ENV=development
    networks:
      - nginx-proxy

  nginx-proxy:
    image: jwilder/nginx-proxy
    ports:
      - 80:80
      - 443:443
    restart: always
    volumes:
      - /nginx/certs:/etc/nginx/certs
      - /nginx/vhost.d:/etc/nginx/vhost.d
      - /nginx/html:/usr/share/nginx/html
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - /home/ubuntu/custom.conf:/etc/nginx/conf.d/custom.conf
    labels:
      - com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy
    networks:
      - nginx-proxy

  letsencrypt-nginx-proxy:
    image: jrcs/letsencrypt-nginx-proxy-companion
    restart: always
    volumes:
      - /nginx/certs:/etc/nginx/certs
      - /nginx/vhost.d:/etc/nginx/vhost.d
      - /nginx/html:/usr/share/nginx/html
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /nginx/acme:/etc/acme.sh
    networks:
      - nginx-proxy

# volumes:
#   data/redis:

networks:
  nginx-proxy:
    name: nginx-proxy
  # redis-connect:
  #   driver: bridge
  • docker-compose파일은 도커 내에서 다중 컨테이너를 사용할 수 있게끔 설정하는 파일입니다. docker-compose파일을 구성할 때는 공백 간격도 반영되기 때문에 띄어쓰기에 주의를 요합니다.
  • services : 하나의 컨테이너를 생성합니다.
  • image : ecr의 가장 최신 버전을 가져옵니다.
  • environment : 서비스 환경 정보를 입력합니다.
  • nginx-proxy : proxy 서버를 생성합니다.

 

지금까지 배포를 위한 기본적인 script 구조와 절차에 대하여 알아보았습니다. 구조화된 deploy 코드를 바탕으로 ec2, s3, ecr, DB를 새로 구성할 때마다 변경해주면서 사용할 수 있습니다. 

'서버배포 > docker' 카테고리의 다른 글

Proxy & NginX  (0) 2021.03.18
DevOps - Docker & ECR  (0) 2021.03.18
Comments