์ƒ์„ธ ์ปจํ…์ธ 

๋ณธ๋ฌธ ์ œ๋ชฉ

์ฟ ๋ฒ„๋„คํ‹ฐ์Šค : Terraform์œผ๋กœ ์ธ์Šคํ„ด์Šค ๊ด€๋ฆฌ + Kubespray์™€ Ansible๋กœ ๋งˆ์Šคํ„ฐ1๋Œ€, ์›Œ์ปค 2๋Œ€ ์—ฐ๊ฒฐ

๐Ÿ˜Ž ์ง€์‹ in Action/โ˜๏ธ ๐Ÿ–ฅ๏ธ

by :ํ•ดํ”ผ๋ž˜๋น—๐Ÿพ 2025. 4. 16. 01:22

๋ณธ๋ฌธ

728x90

Terraform ์‚ฌ์šฉํ•ด์„œ EC2 ์ธ์Šคํ„ด์Šค(๋งˆ์Šคํ„ฐ 1๋Œ€ + ์›Œ์ปค ๋…ธ๋“œ 2๋Œ€) ์ƒ์„ฑ 

EC2(Terraform ์‹คํ–‰ํ•  ๊ณณ)์— IAM ๋“ฑ๋ก (์ž„์‹œ EC2์—์„œ Terraform ์‹คํ–‰์‹œ์ผฐ๋Š”๋ฐ Terraform์„ ์‹คํ–‰ํ•˜๋Š” ๋…ธ๋“œ๊ฐ€ ๋˜ ์žˆ๊ฒ ์ง€?)

๋”๋ณด๊ธฐ

IAM ์—ญํ•  ๋งŒ๋“ค๊ธฐ

AWS ์ฝ˜์†” → ๊ฒ€์ƒ‰์ฐฝ์— IAM ์ž…๋ ฅ → IAM ์ฝ˜์†”๋กœ ์ด๋™

์™ผ์ชฝ ๋ฉ”๋‰ด์—์„œ [์—ญํ• ]  [์—ญํ•  ๋งŒ๋“ค๊ธฐ] ํด๋ฆญ

์‹ ๋ขฐํ•  ์ฃผ์ฒด ์„ ํƒ:
AWS ์„œ๋น„์Šค  EC2 ์„ ํƒ → ๋‹ค์Œ

๊ถŒํ•œ ์„ค์ •: ์•„๋ž˜ ์ค‘ ํ•˜๋‚˜ ์„ ํƒ

์‹ค์Šต : AmazonEC2FullAccess

๋” ์•ˆ์ „ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด: AmazonEC2ReadOnlyAccess + AmazonVPCFullAccess

์ด๋ฆ„: ์˜ˆ) terraform-k8s-role

์™„๋ฃŒ ํ›„ ์—ญํ•  ์ƒ์„ฑ

ec2 ์ƒ์„ฑํ•˜๊ณ  ip์ฃผ์†Œ ํ™•์ธํ•ด์„œ ์ง์ ‘ ๋„ฃ์–ด์ฃผ๊ธฐ ํ—ท๊ฐˆ๋ฆฐ๋‹ค๋ฉด(๋งจ ์•„๋ž˜์— ์ง์ ‘ํ•˜๋Š” ๋ฐฉ๋ฒ•์žˆ์Œ) Terraform ์„ ์‚ฌ์šฉํ•ด๋ณด์ž 

provider "aws" {
  region = "์›ํ•˜๋Š”๋ฆฌ์ „์ž…๋ ฅ" 
}

variable "key_name" {
  default = "ํผ๋ธ”๋ฆญํ‚ค์ด๋ฆ„(๋งˆ์Šคํ„ฐ์™€ ์›Œ์ปค๊ฐ€ ํ†ต์‹ ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ)"
}

variable "my_ip" {
  description = "Your IP address for SSH access"
  default     = "๋‚ด IP ์ฃผ์†Œ/32"  
}

# ๋ณด์•ˆ ๊ทธ๋ฃน ์„ค์ • (๋งˆ์Šคํ„ฐ์™€ ์›Œ์ปค ๋ชจ๋‘ ๊ฐ™์€ ๋ณด์•ˆ ๊ทธ๋ฃน ์‚ฌ์šฉ)
# ๋งˆ์Šคํ„ฐ ๋”ฐ๋กœ ์›Œ์ปค ๋”ฐ๋กœ ํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์„ ๋“ฏ 
resource "aws_security_group" "k8s_sg" {
  name        = "k8s-cluster-sg"
  description = "Security group for Kubernetes cluster"

  # SSH (22) - ๋‚ด IP์—์„œ๋งŒ SSH ์ ‘๊ทผ ํ—ˆ์šฉ
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [var.my_ip]  # ๋‚ด IP ์ฃผ์†Œ์—์„œ๋งŒ SSH ํ—ˆ์šฉ
  }

  # SSH (22) - ๊ฐ™์€ ๋ณด์•ˆ ๊ทธ๋ฃน ๋‚ด์—์„œ SSH ์ ‘๊ทผ ํ—ˆ์šฉ
  ingress {
    from_port                    = 22
    to_port                      = 22
    protocol                     = "tcp"
    source_security_group_id     = aws_security_group.k8s_sg.id  # ๊ฐ™์€ ๋ณด์•ˆ ๊ทธ๋ฃน ๋‚ด์—์„œ ์ ‘๊ทผ ํ—ˆ์šฉ
  }

  # Kubernetes API (6443) - ๊ฐ™์€ ๋ณด์•ˆ ๊ทธ๋ฃน ๋‚ด์—์„œ๋งŒ ์ ‘๊ทผ ํ—ˆ์šฉ
  ingress {
    from_port                    = 6443
    to_port                      = 6443
    protocol                     = "tcp"
    source_security_group_id     = aws_security_group.k8s_sg.id
  }

  # etcd (2379) - ๊ฐ™์€ ๋ณด์•ˆ ๊ทธ๋ฃน ๋‚ด์—์„œ๋งŒ ์ ‘๊ทผ ํ—ˆ์šฉ
  ingress {
    from_port                    = 2379
    to_port                      = 2379
    protocol                     = "tcp"
    source_security_group_id     = aws_security_group.k8s_sg.id
  }

  # ๋ชจ๋“  ์•„์›ƒ๋ฐ”์šด๋“œ ํŠธ๋ž˜ํ”ฝ ํ—ˆ์šฉ
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# ๋งˆ์Šคํ„ฐ ๋…ธ๋“œ ์„ค์ •
resource "aws_instance" "master" {
  ami                        = "ami-AMI๊ณ ์œ ๋ฒˆํ˜ธ(๋ฆฌ์ „์—์†ํ•ด์žˆ๋Š”)"
  instance_type              = "t3.medium"
  key_name                   = var.key_name
  vpc_security_group_ids     = [aws_security_group.k8s_sg.id]  # ๊ฐ™์€ ๋ณด์•ˆ ๊ทธ๋ฃน
  associate_public_ip_address = true

  tags = {
    Name = "k8s-master"
  }
}

# ์›Œ์ปค ๋…ธ๋“œ ์„ค์ •
resource "aws_instance" "worker" {
  count                      = 2
  ami                        = "ami-AMI๊ณ ์œ ๋ฒˆํ˜ธ(๋ฆฌ์ „์—์†ํ•ด์žˆ๋Š”)"
  instance_type              = "t3.large"
  key_name                   = var.key_name
  vpc_security_group_ids     = [aws_security_group.k8s_sg.id]  # ๊ฐ™์€ ๋ณด์•ˆ ๊ทธ๋ฃน
  associate_public_ip_address = false

  tags = {
    Name = "k8s-worker-${count.index + 1}"
  }
}

# ๋งˆ์Šคํ„ฐ ๋…ธ๋“œ ํผ๋ธ”๋ฆญ IP ์ถœ๋ ฅ
output "master_public_ip" {
  value = aws_instance.master.public_ip
}

# ์›Œ์ปค ๋…ธ๋“œ ํ”„๋ผ์ด๋น— IP ์ถœ๋ ฅ
output "worker_private_ips" {
  value = [for instance in aws_instance.worker : instance.private_ip]
}

# ๋ชจ๋“  ๋…ธ๋“œ์˜ ํ”„๋ผ์ด๋น— IP ์ถœ๋ ฅ
output "all_private_ips" {
  value = concat(
    [aws_instance.master.private_ip],
    [for instance in aws_instance.worker : instance.private_ip]
  )
}

 

 

cd ~/terraform-k8s
terraform plan  # plan
terraform apply # apply

 

OUTPUT

all_private_ips = [
  "๋งˆ์Šคํ„ฐํ”„๋ผ์ด๋น—IP",
  "ํ”„๋ผ์ด๋น—1 IP",
  "ํ”„๋ผ์ด๋น—2 IP",
]
master_public_ip = "๋งˆ์Šคํ„ฐํผ๋ธ”๋ฆญIP"
worker_private_ips = [
  "ํ”„๋ผ์ด๋น—1 IP",
  "ํ”„๋ผ์ด๋น—2 IP",
]

 

๋งˆ์Šคํ„ฐ ๋…ธ๋“œ์—์„œ

 

 

๋น„๋ฐ€ํ‚ค (Terraform์— ์ ์€ ํผ๋ธ”๋ฆญํ‚ค์— ํ•ด๋‹นํ•˜๋Š” ํ”„๋ผ์ด๋น— ํ‚ค)

 

scp -i ํ”„๋ผ์ด๋น—.pem ํ”„๋ผ์ด๋น—.pem ubuntu@๋งˆ์Šคํ„ฐํผ๋ธ”๋ฆญIP:~/

mv ~/ ํ”„๋ผ์ด๋น—.pem ~/.ssh/ ํ”„๋ผ์ด๋น—.pem
chmod 400 ~/.ssh/ ํ”„๋ผ์ด๋น—.pem

 

pipx, Ansible, Kubespray ์„ค์น˜

 

1. pipx

# pipx ์„ค์น˜ (์•ˆ ๋˜์–ด์žˆ๋‹ค๋ฉด)
sudo apt update
sudo apt install -y pipx
pipx ensurepath

# ํ„ฐ๋ฏธ๋„ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์•„๋ž˜ ๋ช…๋ น์œผ๋กœ ์ฆ‰์‹œ ์ ์šฉ
export PATH="$HOME/.local/bin:$PATH"

 

๋”๋ณด๊ธฐ

๋ฐœ์ƒํ–ˆ๋˜ ์˜ค๋ฅ˜ : 

× This environment is externally managed โ•ฐโ”€> To install Python packages system-wide, try apt install python3-xyz, where xyz is the package you are trying to install. If you wish to install a non-Debian-packaged Python package, create a virtual environment using python3 -m venv path/to/venv. Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make sure you have python3-full installed. If you wish to install a non-Debian packaged Python application, it may be easiest to use pipx install xyz, which will manage a virtual environment for you. Make sure you have pipx installed. See /usr/share/doc/python3.12/README.venv for more information. note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages. hint: See PEP 668 for the detailed specification.

 

์›์ธ : 

Python 3.12 ์ด์ƒ์—์„œ Debian/Ubuntu๊ฐ€ ์‹œ์Šคํ…œ ๋ณดํ˜ธ๋ฅผ ์œ„ํ•ด pip์„ ์ œํ•œํ•˜๋Š” ์ƒˆ๋กœ์šด ์ •์ฑ…(PEP 668)์„ ๋„์ž…ํ•˜๋ฉด์„œ ์ƒ๊น€

 

pip3 install --user ansible์„ ์‹คํ–‰ํ–ˆ์„ ๋•Œ

"์ด ์‹œ์Šคํ…œ์€ ์™ธ๋ถ€ ๊ด€๋ฆฌ๋˜๋ฏ€๋กœ pip๋กœ ์ง์ ‘ ์„ค์น˜ํ•˜์ง€ ๋ง๊ณ , ๊ฐ€์ƒํ™˜๊ฒฝ(venv) ๋˜๋Š” apt/pipx๋ฅผ ์จ๋ผ"

 

ํ•ด๊ฒฐ ๋ฐฉ์•ˆ : 

sudo apt install pipx
pipx install ansible

 

2. Ansible ์„ค์น˜

# Ansible ์„ค์น˜
pipx install ansible

 

# ์‚ฌ์šฉ ์˜ˆ์‹œ
ansible --version

 

3. kubespray ์„ค์น˜

# ์ž‘์—… ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ ๋ฐ kubespray ๋‹ค์šด๋กœ๋“œ
mkdir -p ~/k8s-kubespray && cd ~/k8s-kubespray
git clone https://github.com/kubernetes-sigs/kubespray.git
cd kubespray

# kubespray ์˜์กด์„ฑ ์„ค์น˜๋ฅผ ์œ„ํ•œ ์ „์šฉ venv ์ƒ์„ฑ
python3 -m venv .venv
source .venv/bin/activate

# kubespray requirements ์„ค์น˜
pip install -r requirements.txt

 

 

# kubespray ๋””๋ ‰ํ† ๋ฆฌ ์ง„์ž… ์‹œ์—๋Š” ์˜์กด์„ฑ์šฉ ๊ฐ€์ƒํ™˜๊ฒฝ ํ™œ์„ฑํ™” ํ•„์š”
cd ~/k8s-kubespray/kubespray
source .venv/bin/activate

deactivate

 

hosts.ini ์ƒ์„ฑ
# (.venv) ubuntu@ip-๋งˆ์Šคํ„ฐ:~/k8s-kubespray/kubespray/inventory/mycluster$ ls
nano make-hosts.sh
chmod +x make-hosts.sh

 

make-hosts.sh (OUTPUT์— ๋‚˜์™€์žˆ๋Š” IP ์ฃผ์†Œ ์ฐธ๊ณ ํ•ด์„œ ์ž‘์„ฑ)

#!/bin/bash

# Terraform output์—์„œ ๊ฐ’ ์ฝ๊ธฐ
MASTER_PRIVATE_IP=$(terraform output -raw all_private_ips | awk -F '"' '{print $2}')
WORKER_PRIVATE_IPS=($(terraform output -json worker_private_ips | jq -r '.[]'))

MASTER_PUBLIC_IP=$(terraform output -raw master_public_ip)
KEY_PATH="~/.ssh/๋น„๋ฐ€ํ‚ค.pem"  # ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๊ฒฝ๋กœ ์ˆ˜์ • ๊ฐ€๋Šฅ

# INI ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅ
cat <<EOF > ./inventory/mycluster/hosts.ini
[all]
master ansible_host=๋งˆ์Šคํ„ฐํ”„๋ผ์ด๋น—IP ip=๋งˆ์Šคํ„ฐํ”„๋ผ์ด๋น—IP access_ip=๋งˆ์Šคํ„ฐํผ๋ธ”๋ฆญIP ansible_ssh_private_key_file=~/.ssh/๋น„๋ฐ€ํ‚ค.pem
worker1 ansible_host=์›Œ์ปคํ”„๋ผ์ด๋น—IP ip=์›Œ์ปคํ”„๋ผ์ด๋น—IP ansible_ssh_private_key_file=~/.ssh/๋น„๋ฐ€ํ‚ค.pem
worker2 ansible_host=์›Œ์ปคํ”„๋ผ์ด๋น—IP ip=์›Œ์ปคํ”„๋ผ์ด๋น—IP ansible_ssh_private_key_file=~/.ssh/๋น„๋ฐ€ํ‚ค.pem

[kube_control_plane]
master

[etcd]
master

[kube_node]
worker1
worker2

[k8s_cluster:children]
kube_control_plane
kube_node
EOF

echo "โœ… hosts.ini ์ƒ์„ฑ ์™„๋ฃŒ → ./inventory/mycluster/hosts.ini"

 

 

ansible-playbook ๋ช…๋ น ์‹คํ–‰ํ•˜์—ฌ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํด๋Ÿฌ์Šคํ„ฐ ์„ค์น˜ ์™„๋ฃŒ 
cd ~/k8s-kubespray/kubespray
source .venv/bin/activate

ansible-playbook -i inventory/mycluster/hosts.ini \
  --become --become-user=root cluster.yml

 

 

kubectl ์„ค์น˜ (Kubespray๋Š” kubeadm๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ž๋™์œผ๋กœ ์„ค์น˜ ์•ˆํ•ด์คŒ)
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
echo "$(cat kubectl.sha256)  kubectl" | sha256sum --check
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client

 

KUBECONFIG ์„ค์ •

kubectl get ๋ช…๋ น์–ด๋“ค์ด ํด๋Ÿฌ์Šคํ„ฐ์™€ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•จ 

sudo chmod 644 /etc/kubernetes/admin.conf

echo 'export KUBECONFIG=/etc/kubernetes/admin.conf' >> ~/.bashrc
source ~/.bashrc

 


 

ํŒŒ๋“œ ๊ตฌ์„ฑ ์„ฑ๊ณตํ–ˆ๋‹ค!!!

 

 

๋ณด์•ˆ ๊ทธ๋ฃน์ด๋ž‘ ๋งˆ์Šคํ„ฐ ๋…ธ๋“œ ๊ฐ€์šฉ์„ฑ ๊ด€๋ จํ•ด์„œ ํ•  ๋ง์ด ๋งŽ์ง€๋งŒ 

1๋…„ ํ”„๋กœ์ ํŠธ์ด๋ฏ€๋กœ ๋‚˜์ค‘์— ์ ์šฉํ•˜๋Š” ๊ฑธ๋กœ ํ•˜์ž 

 


inventory ์ž๋™ ์ƒ์„ฑ 

 

1. hosts.txt ์ •์˜ 

cat <<EOF > ../hosts.txt
[๋งˆ์Šคํ„ฐIP]
[์›Œ์ปค1 IP]
[์›Œ์ปค2 IP]
EOF

 

2. SSH Key ์ƒ์„ฑ ๋ฐ hosts.txt์— ์ •์˜๋œ IP ์— ๋ณต์‚ฌ 

1) SSH ๋น„๋ฐ€๋ฒˆํ˜ธ ์—†์ด ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ณต๊ฐœ ํ‚ค๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ • 

ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa

 

2) ์ด ํ‚ค๋ฅผ ๊ฐ ๋…ธ๋“œ๋กœ ๋ณต์‚ฌ 

while read ip; do
    sshpass -p '๋น„๋ฐ€๋ฒˆํ˜ธ' ssh-copy-id -o StrictHostKeyChecking=no root@$ip
done < ../hosts.txt

 

 

3. ์ธ๋ฒคํ† ๋ฆฌ ์ž๋™ ์ƒ์„ฑ ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ : Kubespray๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ธ๋ฒคํ† ๋ฆฌ ์ž๋™ ์ƒ์„ฑ๊ธฐ ์‹คํ–‰ 

IP ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ inventory/mycluster/hosts.yaml ํŒŒ์ผ์„ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์คŒ 

declare -a IPS=([๋งˆ์Šคํ„ฐIP] [์›Œ์ปค1 IP] [์›Œ์ปค2 IP])
CONFIG_FILE=inventory/mycluster/hosts.yaml python3 contrib/inventory_builder/inventory.py ${IPS[@]}

 

์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์— ํ•„์š”ํ•œ ์—ญํ• ์„ ์ž๋™์œผ๋กœ ๋‚˜๋ˆ  ์คŒ

์ฒซ๋ฒˆ์งธ IP : ๋งˆ์Šคํ„ฐ

๋‚˜๋จธ์ง€ : ์›Œ์ปค

 

4. ์„ค์น˜ ์ง„ํ–‰

source .venv/bin/activate
ansible-playbook -i inventory/mycluster/hosts.yaml --become --become-user=root cluster.yml

 

728x90

๊ด€๋ จ๊ธ€ ๋”๋ณด๊ธฐ