매번 작업물이 변경될 때마다 직접 도커를 올리고 받고 실행하기가 번거로울 것입니다.
여러 가지 방법이 있겠지만, 여기서는 배포 스크립트(Python)를 작성하여 파일을 실행시키면 바로 배포가 되도록 만들어 보겠습니다.
velog/deploy.py
파일 생성
#!/usr/bin/env python3 # python3으로 동작하는 스크립트를 실행시킬 셔뱅(shebang)
import os
import subprocess # 다른(새로운) 프로세스를 실행하기 위한 모듈
from pathlib import Path
from decouple import config
HOME = str(Path.home())
IDENTITY_FILE = os.path.join(HOME, '.ssh', 'id_rsa') # ssh key 생성 필요
SERVER_NAME = config('SERVER_NAME') # velog/.env 파일에서 값 불러옴
SERVER_HOST = config('SERVER_HOST')
DOCKER_IMAGE = config('DOCKER_IMAGE')
DOCKER_NAME = config('DOCKER_NAME')
AWS_ECR_REGION = config('AWS_ECR_REGION')
AWS_ECR_USER = config('AWS_ECR_USER')
AWS_ECR_URI = config('AWS_ECR_URI')
# Docker 실행시 줄 옵션들
DOCKER_OPTIONS = [
('--rm', ''), # 컨테이너 종료시 파일 시스템 제거
('-d', ''), # 데몬 실행, 데몬 종료시 컨테이너 제거
('-it', ''), # tty 할당
('-p', '80:80'), # 컴퓨터 포트:컨테이너 포트
('--name', 'velog'), # 도커 컨테이너 이름 지정
]
# 로컬에서 실행
def run(cmd, ignore_error=False):
process = subprocess.run(cmd, shell=True) # args로 설명된 명령 실행
if not ignore_error:
process.check_returncode()
# ssh 이용해 서버에서 실행
def ssh_run(cmd, ignore_error=False):
run(f'ssh -o StrictHostKeyChecking=no -i {IDENTITY_FILE} {SERVER_NAME}@{SERVER_HOST} -C {cmd}',
ignore_error=ignore_error)
# Docker 이미지 생성 및 푸쉬
def local_build_push():
run(f'poetry export -f requirements.txt > requirements.txt') # poetry 라이브러리들을 서버에서 pip로 설치하기 위한 파일 생성
run(
f'aws ecr get-login-password --region {AWS_ECR_REGION} ' # AWS ECR을 통해 Docker 로그인
f'| docker login --username {AWS_ECR_USER} --password-stdin {AWS_ECR_URI}'
)
run(f'docker build -t {DOCKER_IMAGE} .') # Docker 이미지 생성
run(f'docker tag {DOCKER_IMAGE} {AWS_ECR_URI}') # Docker 이미지 태그 생성
run(f'docker push {AWS_ECR_URI}') # Docker 이미지 태그에 푸쉬
# Docker 이미지 풀 및 컨테이너 실행
def server_pull_run():
ssh_run(
f'aws ecr get-login-password --region {AWS_ECR_REGION} '
f'| docker login --username {AWS_ECR_USER} --password-stdin {AWS_ECR_URI}'
)
ssh_run(f'sudo docker stop {DOCKER_NAME}', ignore_error=True) # 기존 Docker 이미지 중지 (원래 없을 경우 에러 무시)
ssh_run(f'sudo docker pull {AWS_ECR_URI}') # Docker 이미지 풀
ssh_run('sudo docker run {options} {tag}'.format( # Docker 컨테이너 실행
options=' '.join(
f'{key} {value}' for key, value in DOCKER_OPTIONS
),
tag=AWS_ECR_URI,
))
# .env파일(.dockerignore에 포함) 따로 복사
def copy_env():
run(f'scp .env {SERVER_NAME}@{SERVER_HOST}:/tmp', ignore_error=True) # 프로젝트 .env파일 서버에 복사
ssh_run(f'sudo docker cp /tmp/.env {DOCKER_NAME}:/srv/{DOCKER_NAME}') # 서버에서 Docker 컨테이너로 .env파일 복사
# Docker 컨테이너에 들어가 runserver 실행
def server_exec():
ssh_run( # Docker 컨테이너 내에서 Django 프로젝트 실행
f'sudo docker exec -d {DOCKER_NAME} python manage.py runserver --settings=config.settings.prod 0.0.0.0:80'
)
if __name__ == '__main__':
try:
print('---- 배포 시작!! ----')
local_build_push()
server_pull_run()
copy_env()
server_exec()
print('---- 배포 완료!! ----')
except subprocess.CalledProcessError as e:
print('---- 배포 실패!! ----')
print('cmd >> ', e.cmd)
print('returncode >> ', e.returncode)
print('output >> ', e.output)
print('stdout >> ', e.stdout)
print('stderr >> ', e.stderr)
deploy.py
실행 권한 추가
# velog/
chmod +x deploy.py
sudo
명령 실행시 비밀번호 입력하지 않아 뜨는 에러는 아래 포스트 참고하여 해결velog/.env
파일에서 필요한 값 추가
# ~/.env
# ...
SERVER_NAME=hg
SERVER_HOST=61.72.40.137
DOCKER_IMAGE=raccoonhj33/velog
DOCKER_NAME=velog
AWS_ECR_REGION=ap-northeast-2
AWS_ECR_USER=AWS
AWS_ECR_URI=<repositoryUri>
SERVER_HOST
) 경우, 사용하는 공유기에 따라 이전 포스트와 다를 수 있습니다!파일 실행 확인
# velog/
./deploy.py
# ---- 배포 시작!! ----
# WARNING! Your password will be stored unencrypted in /home/hyojin/.docker/config.json.
# Configure a credential helper to remove this warning. See
# https://docs.docker.com/engine/reference/commandline/login/#credentials-store
#
# Login Succeeded
# Sending build context to Docker daemon 56.32kB
# Step 1/8 : FROM python:3-slim
# ---> ce689abb4f0d
# Step 2/8 : RUN apt -y -qq update & apt -y -qq dist-upgrade && apt -y -qq autoremove
# ---> Using cache
# ---> 540ab9a5b905
# Step 3/8 : RUN pip install --upgrade pip
# ---> Using cache
# ---> 79b39330431b
# Step 4/8 : COPY ./requirements.txt /tmp/
# ---> Using cache
# ---> 122384ba9ec4
# Step 5/8 : RUN pip install -r /tmp/requirements.txt
# ---> Using cache
# ---> a0a7d91122a5
# Step 6/8 : COPY . /srv/velog
# ---> Using cache
# ---> 518b5c224729
# Step 7/8 : WORKDIR /srv/velog
# ---> Using cache
# ---> e99ff2adb47c
# Step 8/8 : CMD /bin/bash
# ---> Using cache
# ---> 5eb1640aa0a9
# Successfully built 5eb1640aa0a9
# Successfully tagged raccoonhj33/velog:latest
# Using default tag: latest
# The push refers to repository [****************.dkr.ecr.ap-northeast-2.amazonaws.com/velog]
# 53976ff8ef86: Layer already exists
# 5a87dc8aa4f6: Layer already exists
# 9cd3b3af222a: Layer already exists
# c129e97d95e7: Layer already exists
# 9f819dd1bdc7: Layer already exists
# fe796314f3f4: Layer already exists
# a642096e6e01: Layer already exists
# b327e713d8e9: Layer already exists
# c38160dfb10e: Layer already exists
# 9eb82f04c782: Layer already exists
# latest: digest: sha256:bc851c3a23b76090f835739657588b80b2071cf4f963610dc6c29210a8ff7226 size: 2417
# 로컬에서 도커 build & push 완료
# WARNING! Your password will be stored unencrypted in /home/hyojin/.docker/config.json.
# Configure a credential helper to remove this warning. See
# https://docs.docker.com/engine/reference/commandline/login/#credentials-store
#
# Login Succeeded
# velog
# Using default tag: latest
# latest: Pulling from velog
# Digest: sha256:bc851c3a23b76090f835739657588b80b2071cf4f963610dc6c29210a8ff7226
# Status: Image is up to date for ****************.dkr.ecr.ap-northeast-2.amazonaws.com/velog:latest
# ****************.dkr.ecr.ap-northeast-2.amazonaws.com/velog:latest
# 76f682c41fd66f62347baab3c3f9fa425b8dbe6c500fa07b93fc0f2109e21481
# 서버에서 도커 pull & run 완료
# .env 100% 449 57.5KB/s 00:00
# .env 파일 복사 완료
# 서버에서 도커 exec 완료
# ---- 배포 완료!! ----
ip 접속 확인
! 하면서 알게 된 사실
61.72.40.137
)를 써야 하지만, DB 호스트 주소는 내부 ip(192.168.0.4
)로도 연결 가능!