🏗️ IaC (Infrastructure as Code)
역사적 배경
- 수동으로 인프라 관리
-> 초창지기에는 서버를 직접 설치하고 ssh로 접속해서 패키지를 설치했음
-> 이걸 수작업 인프라라고 함
-> 사람마다 설정이 다르고, 반복하기도 어렵고, 실수도 잦아짐
해결책 -> 설정을 코드로 만들자 -> IaC탄생
- 서버, 네트워크 같은 인프라를 코드로 관리하는 개념
- 도커 + IaC조합 예시
-> Tarraform, Pulumi, AWS CDK 등을 써서 사용
-> AWS EC2나 ECS에 도커 컨테이너를 배포하는 환경 자체도 코드로 만듦
🧩 여기서 Tarraform이란?
Tarraform이란? 코드로 인프라를 구성하고 관리할 수 있게 해주는 도구
- 선언형 IaC도구: 리소스를 자동으로 생성/제거
🧩 여기서 Ansible이란?
서버 수가 많아질수로그 설정 복잡도가 높아질수록 유용함
절차형 설정 도구: 서버 내부 설정, 설치 자동화
Tarraform이 만든 EC2 IP를 output으로 저장하고
Ansible에서 그 IP를 동적으로 가져와서 사용하는 완전 자동화 가능
Terraform을 사용해주면 AWS 인프라를 자동으로 만들어주는 로봇 개념임
- 직접 클릭해서 만들 필요 없이 tarraform apply 명령 한 번으로 AWS EC2나 VPC나 보안 그룹 같은 리소스를 자동으로 생성해 주는 도구임
- AWS 계정 정보를 연동해주면 자동 만들 수 있음
즉,
❌ EC2를 AWS 콘솔에서 직접 만들 필요 없어.
✅ Terraform이 EC2를 대신 만들어주는 거야!
3/26
📌 주제 선정
- Flask + Docker 앱을 EC2에 완전 자동 배포
📌 목표
- Terraform: EC2, VPC, 보안 그룹 구성
- Ansible: EC2 접속 → Docker 설치 → Flask 앱 이미지 실행
- Docker: Flask 앱 컨테이너로 실행
- 하지만 여기서 순서는 3 -> 2 -> 1 순서로 진행할 예정임
📌 1차 구성도
[1] Terraform
└─ AWS EC2 생성
└─ 보안 그룹 설정
└─ public IP 출력
[2] Ansible
└─ EC2에 SSH 접속
└─ Docker 설치
└─ Flask 앱 컨테이너 실행
[3] Docker
└─ Flask 앱 빌드 이미지로 실행
Step 1: 전체 폴더 구조 만들기
flask-docker-deploy/
├── terraform/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── ansible/
│ ├── inventory.ini
│ └── playbook.yml
├── app/
│ ├── app.py
│ └── Dockerfile
└── README.md
Step 2: Flask + Docker 앱 만들기 (app/ 디렉터리)
Ubuntu
ip: 211.183.3.218
먼저 IP를 수정해 주고
vi /etc/netplan/00-installer-config.yaml

netplan apply
host-name 변경해 주고
root@m20:~# hostnamectl set-hostname flask
root@m20:~# su
root@flask:~#
자동 재시작 스크립트
sudo tee /root/check_ip_and_restart_docker.sh<<EOF
#!/bin/bash
if ! ip add | grep -q 172.17; then
systemctl restart docker
fi
EOF

2번 기본 vim 편집기까지 먼저 설정해 주기

* * * * * /root/check_ip_and_restart_docker.sh

root@flask:~# curl -fsSL https://get.docker.com -o get-docker.sh
root@flask:~# chmod +x get-docker.sh
root@flask:~#./get-docker.sh
app/ app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Hello from Docker + Flask + EC2!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
app /Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY . .
RUN pip install flask
CMD ["python", "app.py"]

- 위와 같은 위치에 파일이 존재해야 됨.
test start
cd app
docker build -t flask-app .
docker run -p 5000:5000 flask-app

- 세션을 복제해서 확인해 보면 잘 나오는 것을 확인할 수 있음


지금 상태 요약
- Docker 컨테이너 실행됨 ✔️
- Flask 앱도 실행됨 ✔️
- 내부 IP 주소로 접속 가능: http://127.0.0.1:5000, http://172.17.0.2:5000
❓ 브라우저를 열 때는 외부 IP를 사용해야 됨
보통 서버에는 컨테이너 내부에서만 접속 가능하도록 구현
내가 로컬 머신에서 브라우저로 확인하려면 도커 호스트의 공인 IP 또는 localhost를 사용해야 함
컨테이너 안에서 curl을 칠 때는


VM의 공인 IP주소로 확인해 보면 창이 뜨는 걸 확인할 수 있음
외부 IP로 확인을 해야 알 수 있음
당연히 내부 IP는 안됨

간단하게 실행되는 걸 확인했으니까
쉬운 게임을 추가해서 HTML + CSS를 사용해 flask를 더 꾸며주자
전 넌센스 퀴즈를 좋아하니까 넌센스 퀴즈 문제를 내도록 하겠습니다!
app/app.py
from flask import Flask, request, render_template_string, session, redirect, url_for
import random
app = Flask(__name__)
app.secret_key = 'supersecretkey' # 세션에 문제 저장용
# 넌센스 퀴즈 리스트
quiz_list = [
{"question": "계속 거짓말만 하는 사람은?", "answer": "또라이"},
{"question": "코끼리 2마리가 싸웠는데 둘 다 코가 떨어진 것은?", "answer": "끼리끼리"},
{"question": "서울이 춥다를 다섯 글자로 하면?", "answer": "서울시립대"},
{"question": "소고기가 없는 나라는??", "answer": "소고기무국"},
]
# HTML + CSS 템플릿
template = '''
<!DOCTYPE html>
<html>
<head>
<title>넌센스 퀴즈 게임</title>
<style>
body {
font-family: 'Segoe UI', sans-serif;
background: #f9f9f9;
color: #333;
padding: 40px;
max-width: 600px;
margin: auto;
border-radius: 10px;
background-color: white;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
text-align: center;
}
h2 {
color: #ff5e5e;
}
.btn {
background-color: #ff5e5e;
color: white;
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
margin: 10px;
font-size: 16px;
}
.btn:hover {
background-color: #e24e4e;
}
p {
font-size: 20px;
}
</style>
</head>
<body>
<h2>넌센스 퀴즈 게임 <d83e><dd2a></h2>
<p><b>Q. {{ question }}</b></p>
{% if show_answer %}
<p><d83d><dca1> 정답: <b>{{ answer }}</b></p>
{% endif %}
<form method="POST">
{% if not show_answer %}
<input type="submit" name="action" value="정답 보기" class="btn">
{% endif %}
<input type="submit" name="action" value="다음 문제" class="btn">
</form>
</body>
</html>
'''
@app.route('/', methods=['GET', 'POST'])
def quiz():
if 'quiz' not in session or request.form.get('action') == '다음 문제':
session['quiz'] = random.choice(quiz_list)
session['show_answer'] = False
if request.method == 'POST' and request.form.get('action') == '정답 보기':
session['show_answer'] = True
quiz = session['quiz']
return render_template_string(template,
question=quiz['question'],
answer=quiz['answer'],
show_answer=session.get('show_answer', False))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
root@flask:~/app# docker rm -f $(docker ps -qa)
67e2db933b65
31d577fbcd4a
- 먼저 한 번 다 지워주고
root@flask:~/app# docker build -t flask-app .
root@flask:~/app# docker run -p 5000:5000 flask-app
다시 시스템 시작 해주기

- 잘 만들어줬고 생성된 걸 확인할 수 있음
Step 3: Terrafrom 구성하기
📌 2차 구성도
[Terraform]
└── EC2 인스턴스 생성
└── 보안 그룹에서 포트 5000 허용
└── public IP 출력
[Ansible]
└── EC2에 SSH 접속
└── Docker 설치
└── flask-app 컨테이너 실행
[Docker Hub]
└── (옵션) flask-app 이미지 push/pull 가능
Step 1: AWS CLI 설치 (이미 되어 있다면 생략)
root@flask:~# sudo apt install awscli

Step 2: IAM 사용자용 액세스 키 발급받기& 자격 증명 설정
root 계정으로 aws 로그인하기




root@flask:~#aws configure

- 이제 로컬에서 Terraform이 AWS리소스를 만들 수 있게 준비하면 끝임
- 이 상태에서 terraform appky를 하면 진짜 EC2가 자동으로 생기는 걸 볼 수 있음
➡️ 이 정보는 ~/.aws/credentials, ~/.aws/config 파일에 저장돼.
- 사용자 목록으로 가서 확인하기



- terraform, aws cli 같은 터미널 기반 도구에서 사용할 때 사용되는 CLI클릭
- 그리고

- 이 키를 자격 증명 설정 때 입력해야 됨
그리고 이제 프로그래밍 접근용 키를 입력해 주면 됨
- IAM에서 발급받은 Access Key임
root@flask:~#aws configure

Step 3: Terraform 설치
root@flask:~/terraform# sudo snap install terraform --classic
- Terraform 설치(ubuntu 기준)
설치가 되면 terraform -v로 확인하기



- 간단하게 위의 설정만으로 로컬 자동 정보로 AWS와 연동
root@flask:~/terraform# terraform init # 플러그인 설치
- Terraform이 필요란 AWS 플로그인 자동으로 설치했고, 프로젝트를 초기화했다는 뜻
root@flask:~/terraform# terraform plan # 계획 보기
- 코드에서 정의한 리소스와 실제 AWS 상태를 비교해 봤는데, No change는 변경 사항이 없다는 것 임
Step 4: EC2 인스턴스 1개 + 보안 그룹까지 자동으로 만들어지는 설정을 실행

- 먼저 키페어를 확인해 줘야 됨!!
Terraform EC2 생성 코드 (main.tf)
provider "aws" {
region = "ap-northeast-2"
}
# 보안 그룹: SSH(22) + Flask(5000) 포트 허용
resource "aws_security_group" "web_sg" {
name = "flask-sg"
description = "Allow SSH and Flask port"
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "Flask App Port"
from_port = 5000
to_port = 5000
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# EC2 인스턴스 생성
resource "aws_instance" "flask_ec2" {
ami = "ami-0d5bb3742db8fc264" # Ubuntu 20.04 (서울 리전용)
instance_type = "t2.micro"
key_name = "project" # AWS에서 만든 키페어 이름
vpc_security_group_ids = [aws_security_group.web_sg.id]
associate_public_ip_address = true
tags = {
Name = "flask-app"
}
}
root@flask:~/terraform# terraform plan
- 만들 EC2를 미리 보기

오류 /해결방안 -1

root@flask:~/terraform# aws sts get-caller-identity
- 정상적으로 안 받아와 지는 걸 확인할 수 있음

- IAM에서 발급받은 Access Key를 안 넣어줘서 생긴 문제임
root@flask:~#aws configure

root@flask:~/terraform# cat ~/.aws/credentials
- 보면 내가 설정한 Access Key가 잘 있는지 확인할 수 있음

3/27
root@flask:~/terraform# tarraform apply
- 진짜로 만들기



- 내가 설정한 보안그룹도 잘 생성된 걸 볼 수 있음
- 이제 앤서블을 진행하겠습니다~!
현재까지 완료된 상황
- Terraform으로 AWS EC2 인스턴스 자동 생성 완료
- 보안 그룹으로 SSH(22번) + Flask(5000번) 포트 개방됨
- EC2에 접속할 수 있는 키페어도 준비됨 (project.pem)
이제 다음 단계는?
🎯 Ansible로 EC2에 SSH 접속해서 Docker 설치 + Flask 앱 컨테이너 실행하기!
여기서 궁금증 해소 시간을 갖도록 할까요?

root@flask:~/terraform# ls
내가 만들지 않았던 파일인 terraform.tfstate/ terraform.tfstate.backup이 생성된 걸 볼 수 있는데
이건 terraform이 자동으로 만들어주는 파일임. 내가 직접 만들 필요도 없고, 보통은 건드릴 일도 없음
terraform.tfstate은 현재 AWS에 어떤 리소스를 만들었는지 저장하는 실시간 상태 정보 확인
terraform.tfstate.backup은 마지막으로 성공한 상태를 백업해 놓는 것
=> 즉, 돌아갈 수 있는 안전지대 역할을 한다는 의미
root@flask:~/terraform# la
-> 얘는 정확한 명령어는 아니지만 ls -la로 숨긴 파일 포함 + 자세한 정보 전부 표시한다는 의미로
나온 파일들을 차근차근하게 풀어보자
.terraform: terraform이 provider(예를 들어 AWS )플로그인 파일들을 저장하는 공간인데/ terraform init을 하면 자동으로 생성됨
.terraform.lock.hcl: 설치된 provider 버전 정보를 고정해 두는 파일
ex) AWS provider v5.92.0을 사용 중이라면 이 정보가 여기에 기록됨, 그래서 프로젝트를 공유하거나 CI/CD 환경에서 버전 충돌 방지에 사용됨.
Step 4: Ansible 구성하기
Step 1. EC2 Public IP 확인
Public IP를 출력하도록 설정해 주기
terraform/outputs.tf 파일 새로 만들어서 아래 내용 추가해 줘
output "ec2_public_ip" {
value = aws_instance.flask_ec2.public_ip
}
root@flask:~/terraform# terraform refresh
- 상태 최신화/ 여기서도 ip가 출력되긴 함
root@flask:~/terraform# terraform output
- public_ip 출력됨
ec2_public_ip = "52.79.242.206"
- 이제 pem키를 등록하고
Step 2. Ansible 인벤토리 설정
root@flask:~/ansible# vi inventory.ini
[flask]
52.79.242.206 ansible_user=ubuntu ansible_ssh_private_key_file=/root/ansible/project.pem
- Ansible 설치하기
sudo apt update
sudo apt install ansible -y
- ansible에 불러오자
root@flask:~/ansible# vi project.pem
root@flask:~/ansible# chmod 400 project.pem
root@flask:~/ansible# ssh ubuntu@52.79.242.206 -i project.pem

root@flask:~/ansible# ssh ubuntu@52.79.242.206 -i project.pem
sudo apt update
sudo apt install python3-six -y
exit # EC2에서 빠져나오기

ansible -i inventory.ini flask -m ping
여기서 오류가 발생한다면 오류/ 해결방안 - 6으로 가서 확인하기

- 참고로 ansible의 경로를 확인해 보고 이 경로애 ansible이 있어야 잘 되니까 참고 부탁 dream
- ping을 했을 때 pong을 하면 정상적으로 실행된 걸 알 수 있음
✅ 지금까지 한 것
EC2 생성 | Terraform으로 자동 생성 완료 ✅ |
Ansible 연결 | SSH 연결 + ping 성공 ✅ |
Python 문제 | 해결 완료 ✅ |
.pem 관리 | 성공적으로 사용 중 ✅ |
Step 4: Docker + Flask 앱 자동 배포하기
✅ 다음 목표
Ansible 플레이북으로 EC2에 Docker 설치 + Flask 앱 컨테이너 실행까지 자동화
✅진행 흐름 요약
1️⃣ playbook.yml 만들기 | 명령 자동화 파일 작성 |
2️⃣ Docker 설치 | EC2에 도커 설치되게 함 |
3️⃣ Flask 앱 실행 | Docker 컨테이너로 띄우기 |
4️⃣ 브라우저 접속 테스트 | 퍼블릭IP:5000 으로 접속해보기 |
Step 3. Ansible 플레이북 만들기
받아올 이미지가 있어야 되기 때문에 도커 허브에 이미지를 직접 푸시해 주자
- 내가 만든 Flask 앱을 도커 이미지로 만들었다면,
- 그걸 도커 허브에 올려야 다른 서버에 pull이 가능함.
root@flask:~/ansible# docker login -u choyunji
- 도커 로그인
root@flask:~/ansible# docker tag flask-app choyunji/flask-nonsense-quiz
- 이미지 태그
root@flask:~/ansible# docker push choyunji/flask-nonsense-quiz
- 이미지 푸시
root@flask:~/ansible# vi playbook.yml
- name: Flask 앱을 Docker로 자동 배포하기
hosts: flask
become: true
tasks:
- name: apt 업데이트
apt:
update_cache: true
- name: Docker 설치
apt:
name: docker.io
state: present
- name: Docker 데몬 실행 및 부팅 시 자동 시작
service:
name: docker
state: started
enabled: true
- name: Flask Docker 컨테이너 실행
shell: |
docker run -d --rm -p 5000:5000 --name flask-app choyunji/flask-nonsense-quiz
root@flask:~/ansible# ansible-playbook -i inventory.ini playbook.yml


http://52.79.242.206:5000

넌센스 퀴즈 게임 웹앱 뜨면 대성공! 🎉
오류 /해결방안 - 2

UnauthorizedOperation: User ... is not authorized to perform: ec2:CreateSecurityGroup
- IAM 권한 문제
- 이 오류의 핵심은 Terraform이 AWS리소스를 만들려고 했는데, 사용하는 IAM사용자에게 그런 권한이 없어서 막혔다는 의미임

- 권한 추가를 AdministratorAccess로 안 줬었고 다른 걸 줬기 때문에 얘를 추가해줘 보자
오류 /해결방안 - 3

│ Error: creating EC2 Instance: operation error EC2: RunInstances,....api error InvalidAMIID.NotFound: The image id '[ami-0c55b159cbfafe1f0]' does not exist
- 내가 설정한 리전에서는 존재하지 않다는 뜻
서울 리전에서 사용 가능한 최신 Ubuntu AMI로 교체하기

- 여기서 확인해 보면 AMI ID를 알 수 있음
수정하고 다시 terraform apply를 하면


CLI를 통해 잘 생성된 걸 확인할 수 있음
오류 /해결방안 - 4
- pem을 잘못설정하고 파일을 만든 것 같아서 지우고 다시 해주려고 하는데 계속
│ Error: Error acquiring the state lock 이런 오류가 발생하는 걸 볼 수 있음

rm -f terraform.tfstate.lock.info
terraform destroy
를 입력해서 파일이 삭제가 안된다면
terraform destroy -lock=false
을 터미널에 입력해서 강제로 파일을 삭제할 수 있음
✅ 이 옵션은 언제 쓰나?
- Terraform이 .tfstate 파일을 lock(잠금) 중인데,
- 비정상 종료로 lock이 해제되지 않아서
- 강제로 "잠금 무시하고 실행"하는 용도야

이제 파일을 다시 생성시켜 주자.
전에 남긴 내용이랑 비슷하니까 따로 남겨주지는 않겠습니다!
오류 /해결방안 - 5
- 위에 파일을 지워줬는데도 init이 안된다?


- 그냥 깔끔하게 terraform관련된 파일 다 지우면 됨

- 그럼 잘 init 되는 걸 확인할 수 있음
참고로
terraform destroy
terraform init
terraform apply
해주면 재생성
오류 /해결방안 - 6

🔥 요점 요약 (지금까지 한 것들)
- ✅ EC2에 python3, pip3, six 설치 완료
- ✅ Ansible 재설치 완료 (pip3 install ansible)
- ✅ ansible --version 정상 출력됨
- ✅ inventory.ini 설정도 OK
- ✅ 명령어: ansible -i inventory.ini flask -m ping 계속 실패 중
- ✅ 오류는 계속 "ansible.module_utils.six.moves" 모듈 없음
여기서 내 문제점은 최신 버전 ansible을 설치했지만, 구형 버전 ansible을 사용하고 있어서 생긴 문제점임
- 그래서 우선순위만 바꿔준다면 문제는 해결
sudo apt remove --purge ansible -y
sudo rm -rf /usr/bin/ansible /usr/lib/python3/dist-packages/ansible
#apt 버전 완전 제거
sudo apt update
sudo apt install -y python3-pip
#pip3 설치 명령어 ubuntu기준
pip3 install --upgrade ansible
export PATH="$HOME/.local/bin:$PATH"
which ansible
ansible --version
#정상 경로 + 버전 확인까지 수행
ansible -i inventory.ini flask -m ping

- 이제 pong 뜨는 걸 확인
- 되는 걸 확인할 수 있음 ㅎㅎ 드디어 해결
오류 /해결방안 - 7

❌ 에러 요약
pull access denied for choyunji/flask-nonsense-quiz
repository does not exist or may require 'docker login'
➡️ 의미:
docker run 명령어로 choyunji/flask-nonsense-quiz 이미지를 받아오려는데
도커 허브(Docker Hub)에 그 이미지가 없거나 비공개 상태라는 뜻
root@flask:~/ansible# docker login -u choyunji

root@flask:~/ansible# docker login -u choyunji
- 도커 로그인
root@flask:~/ansible# docker tag flask-app choyunji/flask-nonsense-quiz
- 이미지 태그
root@flask:~/ansible# docker push choyunji/flask-nonsense-quiz
- 이미지 푸시
3/28 (발표)
9시 10분까지 제출
11시부터 발표 시작
'AWS Cloud School 8기 > AWS_Toy_Project' 카테고리의 다른 글
게임 개발 중소기업의 AWS 활용: 보안 및 유지보수 최적화/ IAM + 3 tier + peering/ 3인 프로젝트 (2) | 2025.03.19 |
---|