Docker
AWS ec2
ec2 생성하기
- 배포를 하기 위해 먼저
ec2
를 생성해준다.
- 프리 티어인
Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Type
를 사용해 ec2를 생성하도록 하자.

- 프리 티어를 선택해주면 된다. 그리고
검토 및 시작
을 누른다.

보안 그룹
을 편집해주도록 하자.

- 아래와 같이 80 포트를 세팅하고,
검토 및 시작
을 누른다.
- 우리가 배포할 웹서비스에 접근할 수 있도록 미리 열여두는 것이다. 사용자는 프론트를 통해서 접근하므로 http의 기본 포트인 80을 열어주는 것이다.

- 이전에 받아서 사용하던 키페어를 삭제해버렸기 때문에, 새로운 키페어를 다운받았다. 만약 사용하던 키페어가 있다면 굳이 새로 생성하지 않아도 무방하다.

- 아래 링크가 걸려있는 곳을 누르면 내가 생성한 ec2 인스턴스의 상태를 볼 수 있는 창으로 이동하게 해준다.

- ec2의 상태가
실행중
으로 되어 있어야 배포가 가능하다.
keypair 권한 변경하기
- 아까 발급받은 키페어의 권한을 변경해보도록 하겠다.
change mode
의 약자인 chmod
와 소유주만 읽고 쓸 수 있게 해주는 600
을 사용하도록 하겠다.
$ chmod 600 ~{keypair 위치}
-i
옵션을 통해 키페어로 접근하며, ec2의 퍼블릭 ip 주소를 넣어주면 된다.
$ ssh -i ~/.ssh/sparta-docker.pem ec2-user@{public_ip}
- 위의 두 코드를 모두 작성하게 되면 아래와 같은 결과화면이 나오게 된다.

ec2에 docker 설치하기
amazon linux docker install
와 같은 검색어를 통해 구글링해주면 되고, 나는 amazon 공홈에서 정보를 찾아보았다.
- 스크롤을 내리다 보면
Amazon Linux 2에 Docker 설치
라는 탭을 발견할 수 있는데, 이곳에 있는 내용을 토대로 터미널로 가 docker를 설치해주면 된다.
yum
: amazon linux에서 패키지를 관리하는 cli 툴
$ sudo yum update -y <<< 새로 업데이트된 것이 있는지 확인
$ sudo amazon-linux-extras install docker -y <<< 도커 설치

$ sudo service docker start
$ service docker status

- docker가 실행되고 있으니,
docker ps
를 통해 정보를 확인하려 하면 아래와 같이 권한이 없다고 나온다. 이유는 현재 ec2 유저에서 docker를 실행할 권한이 없어서 그런 것이다.

- 아래 코드로 권한을 부여할 수 있다.
$ sudo usermod -a -G docker ec2-user

- 해당 권한이 반영된 shell을 사용하려면
exit
명령어를 통해 한 번 나갔다 다시 들어와야 한다.
- 다시 들어와
docker ps
쳐보면 돌아가고 있는 것은 없지만 아래와 같이 확인할 수 있다. 이렇게 되면 docker 설치는 끝났다.

docker-compose 설치하기
- 아래 코드를 통해 설치할 수 있다.
uname
에 다른 값을 넣을 필요없이 아래 코드 그대로 넣어주면 된다.
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

- 이렇게 한 뒤 터미널에
docker-compose
를 입력하면 설명창이 쭈룩 나오는데, 이러면 잘 설치된 것이다.
docker-compose.yaml 만들기
- 아래의 명령어를 입력해 수정창을 열어준다.
vi
: 쉘스크립트에서 파일을 생성하거나 조작하게 해주는 툴
$ vi docker-compose.yaml

- 위의 화면이 보이면
a
를 입력해준다. 입력하면 하단이 --끼워넣기--
라고 변하게 된다. (혹은 영어로 나올 수도 있다.) 이제 아래의 내용을 복붙해준다.
version: "3.9"
services:
flask:
image: wjdeorms27/docker-practice:1.0.0
ports:
- "80:5000"
mongo:
image: mongo:latest
volumes:
- mongo:/data/db/
ports:
- "27017:27017"
volumes:
mongo:

- 내용 다 적은다음에 끌 때는 어떻게 한다고?
esc
키를 누르고 :
를 입력한 다음, wq
를 입력해주면 된다.
- 처음 했을 때 아래의 에러가 났었다. 이유는 들여쓰기가 잘 되어 있지 않은 부분이 존재해서 였다.

- 그 부분을 수정하고 나서는 이미지를 잘 받아와 아래와 같이 잘 생성된 것을 볼 수 있다.

- 그리고 IPv4 주소를 브라우저에 입력하면 내가 띄운 이미지가 잘 돌아가야 한다. 하지만 나는 되지 않는다. ㅋ
CI/CD
Continuous Integration
- 여러 개발자들이 함께 개발을 하는 과정에서 코드가 잘 작동하는지를 확인하는 것
CI
를 하면 애플리케이션에 대한 새로운 코드 변경사항이 정기적으로 빌드 및 테스트되어 공유 리포지토리에 통합된다.
- CI가 필요한 이유
- 여러 명의 개발자가 같이 일하기 위해서
- 지속적인 통합이 되지 않고 만약 특정한 날에 여러사람이 코드를 병합한다고 할 때, 서로의 작업영역이 충돌이 날 가능성이 존재함
- 문제가 있는 경우를 수정하는게 더욱 복잡해지는 경우가 존재함
- CI 방법
- docker에서의 CI
- docker를 사용하는 이유는 배포를 편리하기 하기 위해서이다.
- 배포를 하기 전에 이
배포를 해도 괜찮은 상태인지 확인
하기 위해서 CI를 사용한다.
Continuous Deployment
- 반영된 소스코드가 실제 서비스에도 자동으로 반영이 되게 하는 것
- docker를 사용하면 Continuous Deployment 과정이 더 쉽고 빠르게 진행된다.
GitHub Action을 사용해 CI 파이프라인 구축
테스트 코드의 중요성
$ pip install pytest
pytest로 테스트해보기
- 기존의 파일 구조에서 app.py를 조금 수정해주고, 테스트 파일을 만들도록 하겠다.
- 그리고 터미널에서
pytest
만 쳐주면 테스트가 진행된다.

- 그리고 모듈을 편하게 사용할 수 있도록
requirements.dev.txt
파일을 만들어주도록 하겠다.
-r requirements.txt
pytest==7.1.2
- 위에서 작성한 코드들을 깃헙으로 올려주도록 하겠다.
GitHub Action 기본 문법
Workflows
: 자동화 하려고 하는 과정들
- 한개 또는 여러 개의 job으로 구성되며, event에 의해서 시작된다.
- 빌드, 테스트, 릴리즈, 배포 등의 작업이라고 생각하면 된다.
Events
: workflow를 trigger되는 행동들
- push, pull request, cronjob 등이 있다.
Jobs
: 동일한 runner에서 실행하려고하는 여러 개의 step의 모임
Steps
: job을 구성하는 한개의 커맨드로 action이거나 shell command로 구성된다.
Actions
: 다른 곳에서 정의된 커맨드의 모음
Runner
: Job이 실행되는 환경
- 실제 life cycle

yaml파일로 github action 정의
- 아래의 코드 구조를 가져야 하며, yaml 파일을 생성해보도록 하겠다.

- yaml 파일은 아래의 것을 사용하였다.
on
: 어떤 events를 가지고 workflow를 실행할 것인가?
- 우리는
push
를 사용하며, main 브랜치
에 푸시가 오면 실행하고 싶어 아래와 같이 설정함
jobs
- python으로 hello world를 출력하는 프로그램을 짠다.
runs-on
: 어떤 runner에서 프로그램을 돌릴 것인지 결정
steps
: job은 여러 개의 step으로 구성되어 있다.
uses
: 다른 사람이 정의해 놓은 것을 사용할 수 있다.
checkout
: 깃헙의 특정 위치로 가달라는 요청을 수행해줌
setup-python
: python 3.8 버전을 깔아달라는 요청 수행
run
: shell command를 사용할 수 있다.
name: learn-github-action
on:
push:
branches:
- main
jobs:
python-hello-world:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: "3.8"
- run: python -c "print('hello world')"
- 이제 위의 yaml 파일을 github에 올리도록 하자.
- 파일을 올린 뒤, action 탭으로 가면 yaml 파일이 실행 완료되어 있고, 그 내용은 아래와 같다.

- 한 번 더 눌러서 안으로 타고 들어가면 아래와 같은 결과가 나오고, hello world를 클릭해보면 출력되는 것을 볼 수 있다.

github action을 통해 CI pipeline 생성하기
- github action은 .github/workflows/ 하위에 yaml 파일을 만든다.
name: ci-pipeline
on:
push:
branches:
- main
jobs:
run-test-code:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: "3.8"
- run: pip install -r requirements.dev.txt
- run: pytest
- 아래의 파일구조를 가지게 된다.
- 앞에서 테스트한 파일은 삭제해주어 트리거가 다시 되지 않도록 해주었다.

- 위의 파일을 깃헙에 올린 뒤, action 탭으로 가 테스트 결과를 확인해보자.

- 이제 깃헙에 코드를 올릴 때마다 자동으로 action에서 테스트를 진행하게 될 것이다.
- 코드가 잘못 되어 있다면 에러가 나는데, 이에 대한 것도 실습해보도록 하겠다.
test_utils.py
의 내용을 일부러 틀리게 적어보았다.
from utils import get_movie_info
def test_get_movie_info():
test_url = "https://movie.naver.com/movie/bi/mi/basic.nhn?code=185293"
title, image, desc = get_movie_info(test_url)
assert title == "내일의 기억ㅇㄹㅇㄹㅇㄹ"
assert image == "https://movie-phinf.pstatic.net/20210406_131/1617688160755B157W_JPEG/movie_image.jpg?type=m665_443_2"
assert desc == """사고로 기억을 잃은 채 깨어 난 수진 옆엔자상한 남편 지훈이 그녀를 세심하게 돌봐주고 있다.그리고 집..."""
- 틀린 내용에 대한 에러가 났고, 이를 정확히 짚어주었다.

메인 브랜치에 push할 때 docker image 생성 및 push
- docker access token을 먼저 발급받아야 한다. 링크를 타고 가서 받으면 된다.
- 박스 처리된 부분에 있는 시크릿키를 복사해서 가지고 있어야 한다.


- 토큰을 발급받은 후, 깃헙으로 돌아가 아래의 작업을 진행한다.



- 동일한 방식으로 나의
dockerhub 아이디
를 넣어주면 된다.
- 이제
ci-pipeline.yaml
파일을 수정해주도록 하겠다.
name: ci-pipeline
on:
push:
branches:
- main
jobs:
run-test-code:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: "3.8"
- run: pip install -r requirements.dev.txt
- run: pytest
build-image:
needs: [run-test-code]
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/docker-memo:latest
-
name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
- 우리가 앞에서 setting에 등록한 토큰과 아이디를
${{ }}
를 통해 불러와 사용하게 된다.
- github에 push를 한 뒤, action 탭에 가서 확인해보면 아래와 같은 화면이 나오게 된다. test를 한 뒤, image가 빌드되도록 연결이 되어 있는데, 이것은 yaml 파일에서 우리가 쓴
with
를 통해 연결된 것이다.

- dockerhub에 가 우리가 만든 이미지가 잘 생성되었는지 확인해보도록 하겠다.

- 이런 의문이 들 수 있다. 이게 진짜 내가 만든 이미지라고? 어떻게 확인하는데?
- 먼저 github action에서
build-image
를 클릭해보면 아래의 정보를 확인할 수 있다. 이 값을 외우고 dockerhub에 가서 비교해보자.

- dockerhub의 이미지를 클릭해 들어가보면 빨간 박스 안에 있는 내용이 있다. 이 부분이 위의 빨간 박스와 같다는 것을 알 수 있고, 우리가 test를 진행하면서 이미지가 생성되도록 한 부분이 제대로 실행된 것을 알 수 있다.

GitHub Action을 사용해 CD 파이프라인 구축
- Continuous deployment의 과정
- 서버에 접속 + docker compose up
- docker이기 때문에 위의 작업만 하면 끝!
- CD를 위해 먼저
docker-compose.yaml
을 서버에 올려주도록 하자.
- ssh key와 서버 주소가 필요하다.
- github secrets에 secret 두개를 추가
PRIVATE_KEY
: 서버접속에 필요한 ssh key
HOST
: 서버 private ip (aws의 IPv4 주소)
Dockerfile
수정
FROM python:3.8-slim AS builder
ADD requirements.txt requirements.txt
RUN pip install -r requirements.txt
FROM python:3.8-slim-buster
COPY --from=builder /usr/local/lib/python3.8/site-packages/ /usr/local/lib/python3.8/site-packages/
ADD templates templates
ADD app.py .
ADD utils.py .
CMD ["python", "app.py"]
docker-compose.yaml
- 아래와 같은 빨간 박스가 남아있다면 꼭 지워줘야 한다. (volumes 또는 templates)

version: "3.9"
services:
flask:
image: wjdeorms27/docker-memo:latest
ports:
- "80:5000"
mongo:
image: mongo:latest
volumes:
- mongo:/data/db/
ports:
- "27017:27017"
volumes:
mongo:
name: ci-cd-pipeline
on:
push:
branches:
- main
jobs:
run-test-code:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: "3.8"
- run: pip install -r requirements.dev.txt
- run: pytest
build-image:
needs: run-test-code
runs-on: ubuntu-latest
steps:
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/docker-memo:latest
-
name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
cd-pipeline:
needs: build-image
name: continuos deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: copy file via ssh password
uses: appleboy/scp-action@master
with:
host: ${{ secrets.HOST }}
username: ec2-user
key: ${{ secrets.PRIVATE_KEY }}
port: 22
source: "docker-compose.yaml"
target: "/home/ec2-user/"
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ec2-user
key: ${{ secrets.PRIVATE_KEY }}
port: 22
script: "docker-compose pull && docker-compose up -d"