๐ค 08. CICD ๊ธฐ๋ณธ
๊ตฌ์ถ์ ์ํ ๋์์ธ ์ปจ์
/
[pdf]
1. Simple Web ๋ฌด์ํ๊ฒ ๋ฐฐํฌํ๊ธฐ
Simple Web ์ ์ ์ ์น ํ์ด์ง๋ก ๊ตฌ์ฑ๋ ํ๋ก์ ํธ์ ๋๋ค. ์ด๋ฒ์ฅ์์๋ ๊ฐ์ฅ ๋จ์ํ ํํ์ ์ ํ๋ฆฌ์ผ์ด์ ์ AWS ์ ๋ฐฐํฌํ๋ ํ์ดํ๋ผ์ธ์ ๊ตฌ์ฑํฉ๋๋ค.
ํ์ดํ๋ผ์ธ ๊ตฌ์ฑ์์ ์ฌ์ฉ๋๋ ๊ธฐ์ ์คํ์ ์๋์ ๊ฐ์ต๋๋ค.
- Terraform
- GitHub Actions
- AWS S3
- AWS CodeDeploy
1. ํ๋ก์ ํธ ํฌํฌ ํ๊ธฐ
GitHub ์์ ํ๋ก์ ํธ ํฌํฌ
GitHub simple-web ์์ ํ๋ก์ ํธ ํฌํฌ โ ํฌํฌ ๋ฒํผ ํด๋ฆญ โ ํฌํฌ ์ด๋ฆ ์ ๋ ฅ โ ํฌํฌ ์์ฑ
gh ๋ช ๋ น์ด๋ฅผ ์ด์ฉํ ํฌํฌ
gh auth status gh auth switch # ํ์ ํ๋ค๋ฉด ์ํ gh repo fork https://github.com/dangtong76/simple-web.git Would you like to clone the fork? Yes # ํด๋ก ๊น์ง ํ๋ฒ์ # git clone https://github.com/<your-github-id>/simple-web.git simple-web์ํฌํ๋ก์ฐ ๋๋ ํ ๋ฆฌ ์์ฑ
mkdir -p .github/workflows mkdir -p xinfra/aws-ec2-singleGit ๋ธ๋์น ํ์ ์ค์
vi ~/.bashrc
# .bashrc ํ์ผ์ ์ถ๊ฐ parse_git_branch() { git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/' } # Git ๋ธ๋์น๋ฅผ ์ปฌ๋ฌ๋ก ํ์ export PS1="\u@\h \[\033[32m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\] $ "์ค์ ์ ์ฉํ๊ธฐ
source ~/.bashrc
2. Terraform ์ฝ๋ ์์ฑ
EC2 ์ธ์คํด์ค ์์ฑ
๋ณด์ ๊ทธ๋ฃน ์ถ๊ฐ (์น์๋น์ค 80, ssh ๋ฐฐํฌ 22, ์ธ๋ถ์ ์)
ํผ๋ธ๋ฆญ IP ์ฃผ์ ์ถ๊ฐ (์๋น์ค์ํ ๊ณต์ธIP)
ssh ๋ฐฐํฌ๋ฅผ ์ํ ํค ์์ฑ
600_ec2.tf ์ ๊ท์์ฑ (infra/cwave-aws-eks/600_ec2.tf)
# TLS ํ๋ผ์ด๋น ํค ์์ฑ (๊ณต๊ฐ ํค ํฌํจ) resource "tls_private_key" "example" { algorithm = "RSA" rsa_bits = 2048 } # AWS์์ ํค ํ์ด ์์ฑ resource "aws_key_pair" "ec2_key" { key_name = "ec2-key" # AWS์์ ์ฌ์ฉํ ํค ํ์ด ์ด๋ฆ public_key = tls_private_key.example.public_key_openssh } # EC2 ์ธ์คํด์ค ์์ฑ resource "aws_instance" "nginx_instance" { ami = "ami-08b09b6acd8d62254" # Amazon Linux 2 AMI (๋ฆฌ์ ๋ณ๋ก AMI ID๊ฐ ๋ค๋ฅผ ์ ์์) instance_type = "t2.micro" key_name = aws_key_pair.ec2_key.key_name # AWS์์ ์์ฑํ SSH ํค ์ ์ฉ security_groups = [aws_security_group.nginx_sg.name] # ์ธ์คํด์ค๋ฅผ ๋ค์ ๋ง๋ค๋ ๋น ๋ฅด๊ฒ ๊ต์ฒด ํ๊ธฐ ์ํ ์ต์ lifecycle { create_before_destroy = true } # User ๋ฐ์ดํฐ ๋ณ๊ฒฝ์์ ์ธ์คํด์ค ์ฌ์์ฑ ์ต์ user_data_replace_on_change = true # EC2 ์์ ์ Nginx ์ค์น ๋ฐ ์คํ์ ์ํ User Data user_data = <<-EOF #!/bin/bash yum update -y amazon-linux-extras install nginx1 -y systemctl start nginx systemctl enable nginx EOF tags = { Name = "nginx-server" Environment = "Production" } } # ์ถ๋ ฅ: EC2 ์ธ์คํด์ค์ ํผ๋ธ๋ฆญ IP ์ฃผ์ output "nginx_instance_public_ip" { value = aws_instance.nginx_instance.public_ip description = "Public IP of the Nginx EC2 instance" } # ์ถ๋ ฅ: SSH ์ ์์ ์ฌ์ฉํ Private Key output "ssh_private_key_pem" { value = tls_private_key.example.private_key_pem description = "Private key for SSH access" sensitive = true }200_sg.tf ์ถ๊ฐ์์ฑ (infra/cwave-aws-eks/200_sg.tf)
# ๋ณด์ ๊ทธ๋ฃน ์ค์ : SSH(22) ๋ฐ HTTP(80) ํธ๋ํฝ ํ์ฉ resource "aws_security_group" "nginx_sg" { name_prefix = "nginx-sg" ingress { description = "Allow SSH" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { description = "Allow HTTP" from_port = 80 to_port = 80 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"] } }์คํ
terraform init terraform plan terraform apply.gitignore ํ์ผ ์์ฑ (์ด๋ฏธ ์์ผ๋ฉด SKIP)
.terraform .terraform.lock.hcl terraform.tfstate .DS_Store terraform.tfstate.backup๋ฆฌํฌ์งํ ๋ฆฌ ๋๊ธฐํ
git add . git commit -am "add terraform code" git push origin main
3. Github Secret ๋ง๋ค๊ธฐ
Secret ๋ชฉ๋ก
IAM User Credential ์ ์ ์์ฑ
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
AWS_HOST (EC2 console ์์ ๋ณต์ฌ)
AWS_KEY (terraform ๋ช ๋ น์ผ๋ก output ์ถ๋ ฅ)
# ์ถ๋ ฅ๊ฒฐ๊ณผ ๋ณต์ฌํด์ ์ ๋ ฅ terraform output ssh_private_key_pemAWS_USER : ec2-user (EC2 ์ธ์คํด์ค์ OS ๊ณ์ )
4. Github Actions ์ํฌํ๋ก์ฐ ์์ฑ
ํ์ผ๋ช : .github/workflows/simple-web-ec2-workflow.yaml ์์ฑ
## .github/workflows/ec2-deploy-tough.yml name: AWS EC2-Deploy with Tough on: push: branches: - main jobs: deploys: runs-on: ubuntu-latest steps: - name: 1.์์ค์ฝ๋ ๊ฐ์ ธ์ค๊ธฐ uses: actions/checkout@v3 - name: 2.AWS ์ ์์ ๋ณด ์ค์ uses: aws-actions/configure-aws-credentials@v3 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-2 - name: 2.SSHํค ์ค์ uses: webfactory/ssh-agent@v0.5.3 with: ssh-private-key: ${{ secrets.AWS_KEY}} - name: 3.ํ์ผ ๋ชฉ๋ก๋ณด๊ธฐ run: | ssh -o StrictHostKeyChecking=no ${{ secrets.AWS_USER }}@${{ secrets.AWS_HOST }} "ls -al /home/${{ secrets.AWS_USER }}" - name: 4.ํ์ผ ์๋ฒ๋ก ๋ณต์ฌ run: | ssh -o StrictHostKeyChecking=no ${{ secrets.AWS_USER }}@${{ secrets.AWS_HOST }} "sudo chown -R ec2-user:ec2-user /usr/share/nginx/html" scp -o StrictHostKeyChecking=no -r ./* ${{ secrets.AWS_USER }}@${{ secrets.AWS_HOST }}:/usr/share/nginx/html - name: Restart Web Server run: | ssh -o StrictHostKeyChecking=no ${{ secrets.AWS_USER }}@${{ secrets.AWS_HOST }} "sudo systemctl restart nginx || sudo systemctl restart httpd"๋ฆฌํฌ์งํ ๋ฆฌ ๋๊ธฐํ
git add . git commit -am "add github actions workflow" git push origin start์ํฌํ๋ก์ฐ ์คํ ๊ฒฐ๊ณผ ํ์ธ
- http://<EC2 ์ธ์คํด์ค์ ํผ๋ธ๋ฆญ IP ์ฃผ์> ์ ์ ์ํ์ฌ ์น ํ์ด์ง ํ์ธ
2. Simple Web ํ๋ช ํ๊ฒ ๋ฐฐํฌํ๊ธฐ
1. EC2์ฉ IAM ์ญํ ์์ฑํ๊ธฐ
EC2์ฉ IAM ์ญํ ์์ฑํ๊ธฐ
AWS ์ฝ์์์ : IAM โ ์ญํ โ ์ญํ ์์ฑ
์ญํ ์์ฑ ํ๋ฉด์์ ์๋์ ๊ฐ์ด [ AWS ์๋น์ค | ์๋น์ค ๋๋ ์ฌ์ฉ์ฌ๋ก = EC2 | ์ฌ์ฉ์ฌ๋ก = EC2 ] ์ ํ โ ๋ค์

AWSCodeDeployFullAccess ๋ฐ AmazonS3FullAccess ์ ์ฑ ์ถ๊ฐ โ ๋ค์

์ญํ ์ด๋ฆ : simple-web-ec2-deploy-role โ ๋ค์

2. CodeDeploy์ฉ IAM ์ญํ ์์ฑ
AWS ์ฝ์์์ : IAM โ ์ญํ โ ์ญํ ์์ฑ
์ญํ ์์ฑ ํ๋ฉด์์ : [AWS ์๋น์ค | ์๋น์ค ๋๋ ์ฌ์ฉ์ฌ๋ก = CodeDeploy | ์ฌ์ฉ์ฌ๋ก = CodeDeploy ]
๋ด์ฉ ํ์ธํ๊ณ โ ๋ค์

์ญํ ์ด๋ฆ : simple-web-codedeploy-role ์ ๋ ฅ โ ์ญํ ์์ฑ ๋ฒํผ ํด๋ฆญ

3. Terrafrom ์ฝ๋๋ก EC2 ์ธ์คํด์ค ์์
์ฝ๋ ์์ด์ ํธ ์ค์น๋ฅผ ์ํ Ruby ๋ฅผ ์ค์น ํฉ๋๋ค.
Codedeploy Agent๋ฅผ ์ค์น ํ ์๋น์ค๋ฅผ ์์ํฉ๋๋ค.
- 600_ec2.tf ์์ (infra/cwave-aws-eks/600_ec2.tf)
resource "aws_instance" "nginx_instance" { subnet_id = aws_subnet.dangtong-vpc-public-subnet["a"].id ami = "ami-08b09b6acd8d62254" # Amazon Linux 2 AMI (๋ฆฌ์ ๋ณ๋ก AMI ID๊ฐ ๋ค๋ฅผ ์ ์์) instance_type = "t2.micro" key_name = aws_key_pair.ec2_key_pair.key_name # AWS์์ ์์ฑํ SSH ํค ์ ์ฉ vpc_security_group_ids = [aws_security_group.nginx_sg.id] iam_instance_profile = aws_iam_instance_profile.ec2_profile.name # ์ธ์คํด์ค๋ฅผ ๋ค์ ๋ง๋ค๋ ๋น ๋ฅด๊ฒ ๊ต์ฒด ํ๊ธฐ ์ํ ์ต์ lifecycle { create_before_destroy = true } # User ๋ฐ์ดํฐ ๋ณ๊ฒฝ์์ ์ธ์คํด์ค ์ฌ์์ฑ ์ต์ user_data_replace_on_change = true # EC2 ์์ ์ Nginx ์ค์น ๋ฐ ์คํ์ ์ํ User Data user_data = <<-EOF #!/bin/bash yum update -y # Ruby ์ค์น yum install -y ruby wget # CodeDeploy Agent ์ค์น cd /home/ec2-user wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install chmod +x ./install ./install auto # CodeDeploy Agent ์๋น์ค ์์ systemctl start codedeploy-agent systemctl enable codedeploy-agent # nginx ์ค์น amazon-linux-extras install nginx1 -y systemctl start nginx systemctl enable nginx EOF tags = { Name = "nginx-server" Environment = "Production" } }
4. EC2 ์ธ์คํด์ค์์ ์ญํ ์ฐ๊ฒฐํ๊ธฐ
EC2 โ ์ธ์คํด์ค โ ์ธ์คํด์ค ID โ ์์ โ ๋ณด์ โ IAM ์ญํ ์์ โ IAM ์ญํ ์ ํ โ simple-web-ec2-deploy-role ์ ํ โ IAM ์ญํ ์ ๋ฐ์ดํธ

5. S3 ๋ฒํท ๋ง๋ค๊ธฐ
S3 ํ๋ฉด์์ โ ๋ฒํท ๋ง๋ค๊ธฐ ํด๋ฆญ
๋ฒํท์ด๋ฆ : simple-web-content
๋๋จธ์ง๋ ๋ชจ๋ Default ๋ก ์์ฑ

6. CodeDeploy ์ ํ๋ฆฌ์ผ์ด์ ๋ง๋ค๊ธฐ
codedeploy โ ์ ํ๋ฆฌ์ผ์ด์
โ ์ ํ๋ฆฌ์ผ์ด์
์์ฑ
์ ํ๋ฆฌ์ผ์ด์
์ด๋ฆ : simple-web-content
7. CodeDeploy ๋ฐฐํฌ ๊ทธ๋ฃน ๋ง๋ค๊ธฐ
๋ฐฐํฌ ๊ทธ๋ฃน ์์ฑ
codedeploy โ ์ ํ๋ฆฌ์ผ์ด์ โ ๋ฐฐํฌ ๊ทธ๋ฃน ์์ฑ
๋ฐฐํฌ๊ทธ๋ฃน์ด๋ฆ์ ๋ ฅ : simple-web-deploy-group
์๋น์ค์ญํ : simple-web-codedeploy-role

๊ณ์
์ ํ๋ฆฌ์ผ์ด์ ๋ฐฐํฌ ๋ฐฉ๋ฒ : ํ์ฌ ์์น
ํ๊ฒฝ๊ตฌ์ฑ : Amazon EC2 ์ธ์คํด์ค
ํ๊ทธ ๊ทธ๋ฃน : ํค = Environment | ๊ฐ = Production
๋ก๋ ๋ฐธ๋ฐ์ ์ฒดํฌ ๋ฐ์ค ๋นํ์ฑํ

8. Github Action Secret ์ ๋ฐ์ดํธ
์๋ SECRET ์ ๋ฐ์ดํธ ํด์ฃผ๊ธฐ
AWS_HOST
AWS_BUCKET
9. Github Action Workflow ์์ฑํ๊ธฐ
AWS CodeDploy๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด appspec.yml ๋ฐ๋์ ์์ด์ผ ํฉ๋๋ค.
์๋น์ค๋ฅผ ์ค์ง,์์ ํ๊ธฐ ์ํ ์คํฌ๋ฆฝํธ๋ ํ์ํ๋ฉด ์์ฑํฉ๋๋ค.
- ๊ธฐ์กด ์ํฌ ํ๋ก์ฐ ๋นํ์ฑํ
gh workflow list
gh workflow disable
์๋ก์ด workflow ํ์ผ ์์ฑ
## .github/workflows/ec2-deploy-smart.yml name: AWS EC2-Deploy with Smart on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - name: 1.์์ค์ฝ๋ ๋ค์ด๋ก๋ (simple-web) uses: actions/checkout@v2 - name: 2.AWS CLI ์ ์์ ๋ณด ์ค์ uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-2 - name: 3.์ํฐํฉํธ ๋ง๋ค๊ธฐ run: | pwd zip -r deploy.zip ./* - name: 4.S3 ์ํฐํฉํธ ์ ๋ก๋ run: | aws s3 cp deploy.zip s3://${{ secrets.AWS_BUCKET }}/deploy.zip - name: 5.ํ์ฌ ์งํ์ค์ธ AWS Deploy ID ๊ฐ์ ธ์ค๊ณ ์ค๋จ ์ํจ๋ค. run: | DEPLOYMENTS=$(aws deploy list-deployments \ --application-name simple-web-content \ --deployment-group-name simple-web-deploy-group \ --include-only-statuses "InProgress" \ --query 'deployments[]' \ --output text) if [ ! -z "$DEPLOYMENTS" ]; then for deployment in $DEPLOYMENTS; do echo "Stopping deployment $deployment" aws deploy stop-deployment --deployment-id $deployment done # ์ ์ ๋๊ธฐํ์ฌ ์ทจ์๊ฐ ์๋ฃ๋๋๋ก ํจ sleep 10 fi - name: 6. AWS Deploy๋ฅผ ํตํด ๋ฐฐํฌํ๋ค id: deploy run: | DEPLOYMENT_ID=$(aws deploy create-deployment \ --application-name simple-web-content \ --deployment-group-name simple-web-deploy-group \ --s3-location bucket=${{ secrets.AWS_BUCKET }},key=deploy.zip,bundleType=zip \ --output text \ --query 'deploymentId') #echo "::set-output name=deployment_id::$DEPLOYMENT_ID" #echo "{name}=deployment_id" >> $GITHUB_OUTPUT echo "deployment_id=${DEPLOYMENT_ID}" >> $GITHUB_OUTPUT - name: Wait for deployment to complete run: | aws deploy wait deployment-successful --deployment-id ${{ steps.deploy.outputs.deployment_id }}์คํฌ๋ฆฝํธ ๋ฑ๋ก
scripts/before_install.sh
#!/bin/bash # ๊ธฐ์กด ํ์ผ ์ญ์ if [ -d /usr/share/nginx/html ]; then rm -rf /usr/share/nginx/html/* fiscripts/after_install.sh
#!/bin/bash # ํ์ผ ๊ถํ ๋ณ๊ฒฝ chmod -R 755 /usr/share/nginx/html chown -R nginx:nginx /usr/share/nginx/html # ์น์๋ฒ ์ฌ์์ systemctl restart nginxappspec.yml ๋ฑ๋กํ๊ธฐ
version: 0.0 # AppSpec ํ์ผ ๋ฒ์ os: linux # ๋ฐฐํฌ ๋์ ์ด์์ฒด์ files: # ๋ณต์ฌํ ํ์ผ ๋ฐ ๋๋ ํฐ๋ฆฌ ๋ชฉ๋ก - source: / # ์์ค ๋๋ ํฐ๋ฆฌ(์ ์ฒด) destination: /usr/share/nginx/html/ # ๋ณต์ฌ ๋์ ๊ฒฝ๋ก overwrite: true # ๊ธฐ์กด ํ์ผ ๋ฎ์ด์ฐ๊ธฐ ํ์ฉ permissions: # ํ์ผ ๋ฐ ๋๋ ํฐ๋ฆฌ ๊ถํ ์ค์ - object: /usr/share/nginx/html # ๊ถํ์ ์ ์ฉํ ๋๋ ํฐ๋ฆฌ pattern: "**" # ๋ชจ๋ ํ์ ๋๋ ํฐ๋ฆฌ ํฌํจ owner: nginx # ์์ ์ group: nginx # ๊ทธ๋ฃน mode: 755 # ๋๋ ํฐ๋ฆฌ ๊ถํ type: - directory # ๋๋ ํฐ๋ฆฌ์๋ง ์ ์ฉ - object: /usr/share/nginx/html # ๊ถํ์ ์ ์ฉํ ๋๋ ํฐ๋ฆฌ pattern: "**/*" # ๋ชจ๋ ํ์ผ ํฌํจ owner: nginx # ์์ ์ group: nginx # ๊ทธ๋ฃน mode: 644 # ํ์ผ ๊ถํ type: - file # ํ์ผ์๋ง ์ ์ฉ hooks: # ๋ฐฐํฌ ๋ผ์ดํ์ฌ์ดํด ํ BeforeInstall: # ์ค์น ์ ์คํํ ์คํฌ๋ฆฝํธ - location: scripts/before_install.sh # ์คํฌ๋ฆฝํธ ๊ฒฝ๋ก timeout: 300 # ํ์์์(์ด) runas: root # ์คํ ์ฌ์ฉ์ AfterInstall: # ์ค์น ํ ์คํํ ์คํฌ๋ฆฝํธ - location: scripts/after_install.sh # ์คํฌ๋ฆฝํธ ๊ฒฝ๋ก timeout: 300 # ํ์์์(์ด) runas: root # ์คํ ์ฌ์ฉ์๋ฆฌํฌ์งํ ๋ฆฌ ๋๊ธฐํ ํ๊ธฐ
git add . git commit -am "add github actions workflow" git push origin startAWS EC2 ์ธ์คํด์ค์์ Agent ๋ฐฐํฌ ๋ก๊ทธ ํ์ธํ๊ธฐ
sudo tail -f /var/log/aws/codedeploy-agent/codedeploy-agent.log
3. Simple Web ์๋ฒฝํ๊ฒ ๋ฐฐํฌ ํ๊ธฐ
1. EC2 ์์
# EC2 ์ธ์คํด์ค ์์ฑ
resource "aws_instance" "nginx_instance" {
ami = "ami-08b09b6acd8d62254" # Amazon Linux 2 AMI (๋ฆฌ์ ๋ณ๋ก AMI ID๊ฐ ๋ค๋ฅผ ์ ์์)
instance_type = "t2.micro"
key_name = aws_key_pair.ec2_key.key_name # AWS์์ ์์ฑํ SSH ํค ์ ์ฉ
security_groups = [aws_security_group.nginx_sg.name]
# ์์
-> ๋ณด์ -> IAM ์ญํ ์์
iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
# ์ธ์คํด์ค๋ฅผ ๋ค์ ๋ง๋ค๋ ๋น ๋ฅด๊ฒ ๊ต์ฒด ํ๊ธฐ ์ํ ์ต์
lifecycle {
create_before_destroy = true
}
# User ๋ฐ์ดํฐ ๋ณ๊ฒฝ์์ ์ธ์คํด์ค ์ฌ์์ฑ ์ต์
user_data_replace_on_change = true
# EC2 ์์ ์ Nginx ์ค์น ๋ฐ ์คํ์ ์ํ User Data
user_data = <<-EOF
#!/bin/bash
yum update -y
# Ruby ์ค์น
yum install -y ruby wget
# CodeDeploy Agent ์ค์น
cd /home/ec2-user
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
chmod +x ./install
./install auto
# CodeDeploy Agent ์๋น์ค ์์
systemctl start codedeploy-agent
systemctl enable codedeploy-agent
# nginx ์ค์น
amazon-linux-extras install nginx1 -y
systemctl start nginx
systemctl enable nginx
# ์ฌ์์ฑ์ ์ํ ์์ฝ
echo "1"
EOF
tags = {
Name = "nginx-server"
Environment = "Production"
}
}
2. IAM ์์ฑ
ํ์ผ๋ช : 400_iam.tf ์์ฑ [์ถ๊ฐ]
# GitHub Actions์ฉ IAM ์ญํ ์์ฑ
resource "aws_iam_role" "github_actions_role" {
name = "GithubActionsRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
}
]
})
tags = {
Name = "github-actions-role"
}
}
# CodeDeploy๋ฅผ ์ํ EC2 IAM ์ญํ
resource "aws_iam_role" "ec2_codedeploy_role" {
name = "EC2CodeDeployRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
tags = {
Name = "ec2-codedeploy-role"
}
}
# CodeDeploy ์๋น์ค ์ญํ
resource "aws_iam_role" "codedeploy_service_role" {
name = "CodeDeployServiceRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "codedeploy.amazonaws.com"
}
}
]
})
tags = {
Name = "codedeploy-service-role"
}
}
# GitHub Actions ์ญํ ์ ์ ์ฑ
์ฐ๊ฒฐ
resource "aws_iam_role_policy_attachment" "github_actions_s3" {
role = aws_iam_role.github_actions_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
}
resource "aws_iam_role_policy_attachment" "github_actions_codedeploy" {
role = aws_iam_role.github_actions_role.name
policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployFullAccess"
}
# EC2 ์ธ์คํด์ค ์ญํ ์ ์ ์ฑ
์ฐ๊ฒฐ
resource "aws_iam_role_policy_attachment" "ec2_codedeploy_s3" {
role = aws_iam_role.ec2_codedeploy_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
}
resource "aws_iam_role_policy_attachment" "ec2_codedeploy" {
role = aws_iam_role.ec2_codedeploy_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole"
}
# CodeDeploy ์๋น์ค ์ญํ ์ ์ ์ฑ
์ฐ๊ฒฐ
resource "aws_iam_role_policy_attachment" "codedeploy_service" {
role = aws_iam_role.codedeploy_service_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole"
}
# EC2 ์ธ์คํด์ค ํ๋กํ์ผ ์์ฑ
resource "aws_iam_instance_profile" "ec2_profile" {
name = "EC2CodeDeployProfile"
role = aws_iam_role.ec2_codedeploy_role.name
}
# ์ถ๋ ฅ: GitHub Actions ์ญํ ARN
output "github_actions_role_arn" {
value = aws_iam_role.github_actions_role.arn
description = "ARN of the GitHub Actions IAM Role"
}
3. S3 ์ ๊ท ์์ฑ
ํ์ผ๋ช : 700_s3.tf [์ ๊ท]
# S3 ๋ฒํท ์์ฑ
resource "aws_s3_bucket" "deploy_bucket" {
bucket = "simple-web-deploy-bucket-${data.aws_caller_identity.current.account_id}" # ๊ณ ์ ํ ๋ฒํท ์ด๋ฆ ํ์
}
# S3 ๋ฒํท ๋ฒ์ ๊ด๋ฆฌ ์ค์
resource "aws_s3_bucket_versioning" "deploy_bucket_versioning" {
bucket = aws_s3_bucket.deploy_bucket.id
versioning_configuration {
status = "Enabled"
}
}
# S3 ๋ฒํท ์ด๋ฆ ์ถ๋ ฅ
output "deploy_bucket_name" {
value = aws_s3_bucket.deploy_bucket.id
description = "Name of the S3 bucket for deployments"
}
4. codedeploy ์์ฑ
ํ์ผ๋ช : 800_codedeploy.tf [์ ๊ท]
# CodeDeploy ์ ํ๋ฆฌ์ผ์ด์
์์ฑ
resource "aws_codedeploy_app" "web_app" {
name = "simple-web-content2"
}
# CodeDeploy ๋ฐฐํฌ ๊ทธ๋ฃน ์์ฑ
resource "aws_codedeploy_deployment_group" "web_deploy_group" {
app_name = aws_codedeploy_app.web_app.name
deployment_group_name = "simple-web-deploy-group"
service_role_arn = aws_iam_role.codedeploy_service_role.arn
deployment_style {
deployment_option = "WITHOUT_TRAFFIC_CONTROL"
deployment_type = "IN_PLACE"
}
ec2_tag_set {
ec2_tag_filter {
key = "Environment"
type = "KEY_AND_VALUE"
value = "Production"
}
}
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE"]
}
alarm_configuration {
enabled = false
}
}
# CodeDeploy ์ ํ๋ฆฌ์ผ์ด์
์ด๋ฆ ์ถ๋ ฅ
output "codedeploy_app_name" {
value = aws_codedeploy_app.web_app.name
description = "Name of the CodeDeploy application"
}
# CodeDeploy ๋ฐฐํฌ ๊ทธ๋ฃน ์ด๋ฆ ์ถ๋ ฅ
output "codedeploy_deployment_group_name" {
value = aws_codedeploy_deployment_group.web_deploy_group.deployment_group_name
description = "Name of the CodeDeploy deployment group"
}
5. ์ํฌํ๋ก์ฐ Disable ๋ฐ Github ์ ํ
- ์ํฌํ๋ก์ฐ Disable
gh workflow disable
- Github Secret AWS_BUCKET ๋ณ๊ฒฝ
6. ์ ๊ท ํ์ดํ๋ผ์ธ ์ถ๊ฐ
## .github/workflows/ec2-deploy-smart.yml
name: AWS EC2-Deploy with Perfect
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 1.์์ค์ฝ๋ ๋ค์ด๋ก๋ (simple-web)
uses: actions/checkout@v2
- name: 2.AWS CLI ์ ์์ ๋ณด ์ค์
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2
- name: 3.์ํฐํฉํธ ๋ง๋ค๊ธฐ
run: |
pwd
zip -r deploy.zip ./*
- name: 4.S3 ์ํฐํฉํธ ์
๋ก๋
run: |
aws s3 cp deploy.zip s3://${{ secrets.AWS_BUCKET }}/deploy.zip
- name: 5.ํ์ฌ ์งํ์ค์ธ AWS Deploy ID ๊ฐ์ ธ์ค๊ณ ์ค๋จ ์ํจ๋ค.
run: |
DEPLOYMENTS=$(aws deploy list-deployments \
--application-name simple-web-content2 \
--deployment-group-name simple-web-deploy-group \
--include-only-statuses "InProgress" \
--query 'deployments[]' \
--output text)
if [ ! -z "$DEPLOYMENTS" ]; then
for deployment in $DEPLOYMENTS; do
echo "Stopping deployment $deployment"
aws deploy stop-deployment --deployment-id $deployment
done
# ์ ์ ๋๊ธฐํ์ฌ ์ทจ์๊ฐ ์๋ฃ๋๋๋ก ํจ
sleep 10
fi
- name: 6. AWS Deploy๋ฅผ ํตํด ๋ฐฐํฌํ๋ค
id: deploy
run: |
DEPLOYMENT_ID=$(aws deploy create-deployment \
--application-name simple-web-content2 \
--deployment-group-name simple-web-deploy-group \
--s3-location bucket=${{ secrets.AWS_BUCKET }},key=deploy.zip,bundleType=zip \
--output text \
--query 'deploymentId')
#echo "::set-output name=deployment_id::$DEPLOYMENT_ID"
#echo "{name}=deployment_id" >> $GITHUB_OUTPUT
echo "deployment_id=${DEPLOYMENT_ID}" >> $GITHUB_OUTPUT
- name: Wait for deployment to complete
run: |
aws deploy wait deployment-successful --deployment-id ${{ steps.deploy.outputs.deployment_id }}
7. ์์ค ์์ ๋ฐ ๋ฐฐํฌ
simple-web/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Last</title>
<link rel="stylesheet" href="style.css" />
<link
- ์ปค๋ฐ ๋ฐ Push
git commit -am "deploy perfect"
git push origin main
4. Simple WEB ์ฟ ๋ฒ๋คํฐ์ค์ ๋ฉ๋ด์ผ ๋ฐฐํฌํ๊ธฐ
1. Docker ํ์ผ ์์ฑ
FROM nginx:alpine
# ๊ธฐ์กด nginx ๊ธฐ๋ณธ ํ์ด์ง ์ ๊ฑฐ
RUN rm -rf /usr/share/nginx/html/*
# ์น ํ์ผ๋ค์ ์ปจํ
์ด๋๋ก ๋ณต์ฌ
COPY index.html /usr/share/nginx/html/
COPY style.css /usr/share/nginx/html/
COPY assets/ /usr/share/nginx/html/assets/
COPY scripts/ /usr/share/nginx/html/scripts/
# nginx ํฌํธ ๋
ธ์ถ
EXPOSE 80
# nginx ์คํ
CMD ["nginx", "-g", "daemon off;"]
2. ์ปจํ ์ด๋ ์ด๋ฏธ์ง ๋น๋
docker build -t <your-dockerhub-id>/simple-web .
3. ์ปจํ ์ด๋ ์ด๋ฏธ์ง ํธ์
docker login --username <your-dockerhub-id>
docker push <your-dockerhub-id>/simple-web
4. ์ฟ ๋ฒ๋คํฐ์ค ๋ฐฐํฌ
- simple-web-deploy.yaml ํ์ผ ์์ฑ
apiVersion: apps/v1
kind: Deployment
metadata:
name: simple-web
labels:
app: simple-web
spec:
replicas: 3
selector:
matchLabels:
app: simple-web
template:
metadata:
labels:
app: simple-web
spec:
containers:
- name: simple-web
image: <your-dockerhub-id>/simple-web
ports:
- containerPort: 80
- simple-web-ingress.yaml ํ์ผ ์์ฑ
apiVersion: v1
kind: Service
metadata:
name: simple-web-lb
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
type: LoadBalancer
selector:
app: simple-web
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 800
ports:
- name: simple-web
protocol: TCP
port: 80
targetPort: 80
5. Github workflow ์์ฑ
- Github Action Secret ์์ฑ
DOCKER_USERNAME
DOCKER_TOKEN # Docker hub Token
PAT # Github token - yml ํ์ผ ์์ฑ
name: simple-web-eks-ci
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
actions: read
jobs:
build:
runs-on: ubuntu-latest
env:
DOCKER_IMAGE: ${{ secrets.DOCKER_USERNAME }}/simple-web
DOCKER_TAG: ${{ github.run_number }}
steps:
- name: 1.์์ค์ฝ๋ ๋ค์ด๋ก๋
uses: actions/checkout@v4
- name: 7.Docker Image Build
run: docker build -t ${{ secrets.DOCKER_USERNAME }}/simple-web:${{ env.DOCKER_TAG }} .
- name: 8.Docker Login
uses: docker/login-action@v3.0.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
logout: true
- name: 9.Docker Push
run: |
docker push ${{ secrets.DOCKER_USERNAME }}/simple-web:${{ env.DOCKER_TAG }}
# ์๋น์ค ๋ฆฌํฌ์งํ ๋ฆฌ ์ฒดํฌ์์
- name: 10.์๋น์ค ๋ฆฌํฌ์งํ ๋ฆฌ ์ฒดํฌ์์
uses: actions/checkout@v4
with:
repository: dangtong-s-inc/simple-service
ref: main
path: .
token: ${{ secrets.PAT }}
# ์ด๋ฏธ์ง ํ๊ทธ ์
๋ฐ์ดํธ
- name: 11.์ฟ ๋ฒ๋คํฐ์ค ๋งค๋ํ์คํธ ํ์ผ ์ด๋ฏธ์ง ํ๊ทธ ์
๋ฐ์ดํธ
run: |
# ํ์ผ์ด ์กด์ฌํ๋์ง ํ์ธ
ls -la
# ํ์ฌ ํ์ผ ๋ด์ฉ ํ์ธ
cat simple-deploy.yaml
sed -i "s|image: ${{ secrets.DOCKER_USERNAME }}\/simple-web.*|image: ${{ secrets.DOCKER_USERNAME }}\/simple-web:${{ env.DOCKER_TAG }}|g" simple-deploy.yaml
# ๋ณ๊ฒฝ๋ ๋ด์ฉ ํ์ธ
cat simple-deploy.yaml
# ๋ณ๊ฒฝ์ฌํญ ์ปค๋ฐ ๋ฐ ํธ์
- name: 12.์๋น์ค ๋ฆฌํฌ์งํ ๋ฆฌ ๋ณ๊ฒฝ์ฌํญ ์ปค๋ฐ ๋ฐ ํธ์
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git commit -am "Update image tag to ${{ env.DOCKER_TAG }}"
git remote set-url origin https://${{ secrets.PAT }}@github.com/${{ secrets.DOCKER_USERNAME }}/simple-service.git
git push origin main