본문 바로가기
Development(Web, Server, Cloud)/Cloud : 정리

IaC : Ansible, Vagrant, Terraform

by tonyhan18 2022. 5. 19.
728x90

사전 설정

문제는 DC에 너무 많은 compute node들이 존재한다. 이들이 관리가 안된다. 그래서 우리가 필요하다고 느끼는 노드에 가서 하이퍼 바이저를 이용하여 VM을 생성하라고 요청을 받으면 하이퍼바이저가 위쪽에 vResource가 할당되고 그 위에 VM이 생성된다.

그래서 전체를 클러스터로 묶고, 전체를 오케스트레이션 하기 위한 도구가 필요해지는데 이 도구를 우리는 controller라고 부른다.

 

보통 DC에는 노드들이 위치하게 되는데 하나는 compute 노드(HV 노드 : 인스턴스, vNetwork 만듬), Controller를 이용한 오케스트레이션을 한다, 관리자는 controller에 작업지시를 해서 인스턴스에 작업을 수행한다.

 

이때의 controller 노드에도 오케스트레이션, 인증 도구, 사용자 UI들이 들어가게 된다.

* 사용자 UI 의 경우 외부에서 서비스를 사용하려고 하는 일반 사용자가 들어올 수도 있다. 이들을 위한 웹페이지 같은 거다

 

스토리지 노드, 인스턴스 생성시 볼륨을 제공할 수 있어야 한다. 그 볼륨의 디스크를 물리자원의 일부를 잘라서 제공하는 방식이다. 이 디스크는 로컬 디스크가 된다. 이때에 또 중요한 기술이 마이그레이션이다. XML 언어로 작성된 VM은 로컬 디스크가 아닌 원격 디스크에 연결되어 저장소를 공급받을 수 있어야 한다. 스토리지는 인스턴스에게 영구볼륨을 제공(NFS, iSCSI, Ceph, GlusterFS, aws ebs, aws efs)

 

전체 클러스터를 오버레이 네트워크로 만들고 외부 사용자의 접근을 적절한 인스턴스로 연결하기 위한것, 인스턴스간의 소통을 위한 것, 인스턴스들을 하나의 tennant, project에 포함시킬 네트워크 노드가 필요하다. 이걸 위해 터널링, DHCP, NAT(fip, eip), OpenSwitch(linux bridge)

 

정리하면 클라우드형 DC 구축시에는 compute, controller, storage, network가 필수요소이다.

 

그럼 앞서 말했던것과 같이 CentOS가 있고 이 내부 커널에 KVM 을 설치해주자. KVM 위쪽에 인스턴스들을 배포해주자. 그래서 이 인스턴스를 포함한 환경을 관리할 수 있는 도구가 필요하다. 이때 쓰는것이 Vagrant이다. Vagrant를 이용해서 환경 명세서를 전달해주는 것이다. 그러면 vagrant는 명세서를 보고 환경을 구성해주는데 이 환경을 위해 libvirt나 ESXi, Virtual Box, aws CloudFormation(템플릿, yaml) -> ec2 이용해서 instance 생성 -> EKS를 불러와서 인스턴스들 묶기. Openstack의 HEAT로도 가능. 환경이 구성되면 이것들을 관리할 필요가 있어진다.

 

그런데 인스턴스가 정말 많으면 이것들을 관리하기 위한 도구로 어떤것을 이용할 것인가? 각각의 노드들에 똑같은 명령을 전달하는 방법으로 SSH가 있을 수도 있지만 Ansible을 이용해서 멱등성과 관리의 수월성등을 도모할 수 있다.

 

추가적으로 배포한 서버들에 대해서 전체서버관리도구(Ansible)와 개발자 컴퓨터를 연결해 놓는다. 트리거를 걸어놓아서 새로운 commit이 발생했다면 이걸 ansible에게 전달하고 ansible은 새로운 commit을 가져와서 해당 코드를 서버에 배포하라는 것을 구성할 수도 있다.

ansible이 새로운 commit을 긁어와서 업데이트가 필요한 노드에 새로운 코드로 업데이트 하라고 던져주는 것이다. 그러면 노드 내의 인스턴스들은 새로운 버전으로 업데이트가 되는 CI/CD 연계도 해줄 수 있다.

결국 이걸 위해서는 CI/CD 도구도 필요해진다. 이때의 CI/CD 도구는 gitlab, github, jenkins를 이용해주자

 

CentOS

- 커널 버전 업데이트

```

yum install -y ssh

```

서버포럼 - Cent OS 커널 최신버전으로 업데이트 하기. (svrforum.com)

 

서버포럼 - Cent OS 커널 최신버전으로 업데이트 하기.

안녕하세요 달소입니다. aaPanel을 접한 후 원활한 서버관리를 위해서 서버들을 Cent OS로 전환하고있는데요. 이전에 Ubuntu에서 사용하던 BBR 부터 Wireguard VPN등은 최신버전의 커널을 사용하기때문에

svrforum.com

먼저 CentOS 7의 커널 버전을 5.0 이상으로 무조건 업데이트 해주어야 한다.

 

# 명령을 실행하여 CentOS 7에 ELRepo 저장소를 추가해주고 서명키를 가져옵니다.
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm

# 이제 설치할 커널버전을 확인해보겠습니다.
# 저는 롱텀버전인 lt를 사용했지만 메인라인을 사용하기 원하시는 분들은 ml 으로 변경해주시면됩니다.
sudo yum --disablerepo="*" --enablerepo="elrepo-kernel" list available | grep kernel-lt

# 이제 설치를 진행하겠습니다.
sudo yum --enablerepo=elrepo-kernel install -y kernel-lt

# 추가로 커널동작에 필요한 다른 패키지들도 설치해줍니다.
sudo yum -y --enablerepo=elrepo-kernel install kernel-lt-{devel,headers,perf}

```

vi /etc/default/grub

```

타임아웃 아래에 GRUB_DEFAULT=0 으로 고정해줍니다.

 

# 그리고 아래 명령어로 컨피그 파일을 재생성 해주고 커널목록을 확인해보겠습니다.
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
grep ^menuentry /boot/grub2/grub.cfg | cut -d "'" -f2

# 첫번째로 나온 5.4버전으로 default 값을 넣겠습니다.
grub2-set-default "CentOS Linux (5.4.130-1.el7.elrepo.x86_64) 7 (Core)"

 

---

 

- 사전 설치

uname -nr
#로 현재 사용하는 OS의 커널 버전을 확인가능하다.

yum install -y wget curl vim git
systemctl stop firewalld ; systemctl disable firewalld
systemctl stop NetworkManager ; systemctl disable NetworkManager

# 문제는 아래의 것은 임시 방편이기 때문에 고정을 시키어 주어야 한다
echo "1" > /proc/sys/net/ipv4/ip_forward

.bashrc에도 위와같이 alias를 미리 작성해 놓자

```

source ~/.bashrc

```

 

 

인스턴스에 볼륨을 저장하기 위한 저장소로 기본 저장소를 사용하는 것이 아니라, 별도로 지정한 디렉토리를 사용하기 위해서는 SELinux가 중지되어 있어야 한다.

setenforce 0

vi /etc/selinux/config

 

볼륨을 저장하기 위한 저장소 디렉토리 생성

KVM -> 인스턴스생성(ansible-server, ansible-node)

mkdir /shared
chmod 777 /shared
yum -y install epel-release
yum -y install qemu-kvm libvirt virt-install openssh-askpass libguestfs-tools libguestfs-xfs virt-top htop
vi /etc/libvirt/qemu.conf  # KVM 데몬을 root로 동작시키기 위한 설정

442, 446에 있는 "#"를 제거 

 

- wok 설치

yum install -y https://github.com/kimchi-project/kimchi/releases/download/2.5.0/wok-2.5.0-0.el7.centos.noarch.rpm
yum install https://github.com/kimchi-project/kimchi/releases/download/2.5.0/kimchi-2.5.0-0.el7.centos.noarch.rpm

systemctl enable wokd
systemctl start wokd

session 유지를 위한 설정, 토큰의 유효기간이 따로 설정되어 있다.

 

 

vi /etc/wok/wok.conf

systemctl restart wokd

 

- 리눅스 브릿지를 이용한 가상 네트워크 구성 

kvm에서 이용하는 default 네트워크와 리눅스 브릿지가 있을때

 

default network는 virbr0 브릿지를 이용하게 된다. 별도의 리눅스 브릿지를 사용하게 된다면 virbr1를 사용하게 된다. virbr0는 host 위쪽에 배치가 된다. eth0와 virbr0가 연결되고 나머지 가상머신들이 eth0와 연결된 구조이다.

 

그러면 virbr0의 네트워크가 구분된 구조가 된다. 이렇게 된다면 1 인터페이스 = 1 네트워크 = 1 Broadcast Domain이 된다. 인스턴스가 나가기 위해서는 NAT를 타야하는데 virbr0는 결국 NAT의 역활을 하게 되는 것이다. 이러한 방식을 Routed Port라고 부른다.

 

이와 다르게  우리는 linux bridge에서 virbr1에 eth0를 집어넣을 것이다. 스위치에 들어가는 한개의 포트는 L2가 된다. 그래서 이 부분에 ip는 안들어가고 관리용으로 사용하게 된다. 이때 vlan1을 사용해서 관리하게 된다. 

 

이때의 각 포트는 동일 vlan의 동일 ip 대역인 L2 포트가 된다. 즉 동일네트워크이니 BroadCast Domain이 된다. 이것은 Bridge를 쓰는 방법이라고 부른다.

 

1. VM에 할당되는 IP주소는 211.183.3.0/24 대역내에서 할당을 받게 된다.

이때 DHCP로 받은 ip 주소가 쉽게 받기지 않도록 VMware Workstation에서 조정해주자.

 

2. br0(linux bridge)만들고 기존 물리 인터페이스를 br0에 포함시켜보자. 단, 현재 인터페이스 이름이 ens32와 같은 이름이므로 이를 eth0으로 수정 한뒤, br0에 포함시킨다.

 

2.1 ens32 -> eth0

eth0의 이름도 수정해주고 필요없는 부분도 없애주자

 

2.2 vi /etc/default/grub

vi /etc/default/grub

GRUB_CMDLINE_LINUX 끝에 net.ifnames=0 biosdevname=0를 추가해주자 이렇게 해야만 인터페이스 이름이 바뀐다. 안해주면 위에서 해주었던 eth0의 이름이 안 바뀐다.

 

그리고 이걸 부팅 파일에 적용해주어야 한다.

 

grub2-mkconfig -o /boot/grub2/grub.cfg

하고 재부팅하여 `ip a` 했을 때, eth0에 211.183.3.77이 등록되어 있고 ping이 가능해야 한다.

 

 

3. br0 생성하고 eth0을 br0의 포트로 등록 시키기

cd /etc/sysconfig/network-scripts/

cp ifcfg-eth0 ifcfg-br0

vi ifcfg-br0

마지막으로 ifcfg-eth0의 내용을 바꾸어 주기만 하면 된다.

 

systemctl enable network ; systemctl restart network

그 결과 eth0에는 ip가 없고 br0에는 ip가 있으면 된다.

 

brctl show

를 했을때 br0가 보이면 된다.

 

볼륨을 준비해보자, 인스턴스 만들기

KVM에서 VM 생성하기

1. iso를 통한 설치 -> 사용의 요구에 맞는 상세한 설정이 가능하지만, 시간이 오래걸린다. 다수의 인스턴스, VM을 운영해야하고 사용자가 요구하는 즉시 서비스를 제공해 주어야 하는 클라우드 환경에서는 적절한 방법이 아니다.

 

2. cloud img를 이용한 방법

qcow2 이미지를 이용하여 이를 배포하는 방법. 그냥 배포하면 디스크 사이즈가 684 MB가 되므로 이를 확장해야 한다. virt-resize를 이용하여 디스크를 확장시켜 사용해야 한다.

 

3. virt-builder 를 이용하는 방법

저장소에 있는 클라우드용 이미지 템플릿을 다운로드 하면서 디스크 사이즈 지정, 루트 패스워드 지정, 파일 복사 | 수정, 명령 실행, 인스턴스 내에서 처음 부팅될 때 실행할 명령어등의 지정이 가능한 방법.

 

 

Vagrant

1. 설치

# 패키지 설치
yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
yum -y install libvirt-daemon-kvm libvirt-client vagrant gcc-c++ make libstdc++-devel libvirt-devel

# libvirtd와 vagrant 실행
systemctl restart libvirtd
vagrant plugin install vagrant-libvirt

vagrant -v

 

 

libvirtd 환경에서 테스트로 인스턴스를 하나 생성해보기

Discover Vagrant Boxes - Vagrant Cloud (vagrantup.com)

 

Vagrant Cloud by HashiCorp

Vagrant Cloud by HashiCorp

app.vagrantup.com

위의 링크로 가보면 다양한 vagrant의 프로비저닝 된 이미지들을 확인해볼 수 있다.

 

2. 기본

vagrant init centos/7

위와같이하면 centos/7 기반의 Vagrantfile이 만들어진다.

원하지 않는다면 그냥 `vagrant init` 해주면 된다.

자동으로 프로비저닝 된 파일을 확인해볼 수 있다.

 

vagrant up

이 명령은 무조건 Vagrant가 있던곳에서만 해야하다보니 한번 시작하면 옮기기 힘들다.

하고나면 위와같이 .vagrant 파일이 보인다.

vagrant box list

하면 우리가 설치했던 파일이 보인다.

virsh list

하면 우리가 vagrant init 때 요청했던 vm이 돌아가는 것을 볼 수 있다.

 

vagrant ssh

vagrant로 만든 가상머신의 이름이 정해져 있지 않다면 이 간단한 명령어로 defualt 가상머신에 들어갈 수 있다.

 

하면 해당 vm에 접속할 수 있다. vagrant vm이 많으면 직접 이름을 지칭해야겠지만 지금은 한개뿐이기 떄문에 이것만으로도 접속이 가능해진다.

 

vagrant vm의 ip는 DHCP로 자동 할당받으며 virbr1의 주소인 192.168.122.1을 통해 외부와 통신할 수 있는 NAT 브릿지로 연결된다.

 

exit # vagrant에서 빠져나오기

vagrant halt
vagrant destroy

default 가상머신을 없앨 수 있다.

 

vagrant 파일안에 있는 35번쨰 줄의 주석을 제거해보자. 그러면 별도로 네트워크가 지정되어 가상머신에는 추가적인 인터페이스가 생성되며 지정된 IP를 할당 받게 된다. 사설 주소이므로 기존의 인터페이스와 별도의 인터페이스를 갖게 된다.

 

생성후 ip는 192.168.33.10에 할당되었으며 사설 주소이므로 기존의 인터페이스와 별도의 인터페이스를 갖게 된다.

 

3. 실습 - 기본 Vagrant 생성하기

이제 Vagrantfile 내부를 모두 지우고 이렇게 작성해주자

# -*- mode: ruby -*-
# # vi: set ft=ruby :

###### ANSIBLE SERVER CONFIG #######

Vagrant.configure("2") do |config|
  config.vm.define "ansible-server" do |cfg|
    cfg.vm.box = "centos/7"
    cfg.vm.host_name = "ansible-server"
    cfg.vm.network "private_network", ip: "172.16.1.10"
    cfg.vm.network "forwarded_port", guest: 22, host: 60010, auto_correct: true, id: 'ssh'
  end
end

위와같이 Vagrantfile을 작성해주자

 

그러면 배포시 ip와 포트번호가 설정된 상태로 배포되는 것을 확인해볼 수 있을 것이다.

vagrant up

하면 생성된거 확인할 수 있으니 다시 내려주고

 

---
- name: test configuration
  hosts: localhost
  gather_facts: no

  tasks:
    - name: install git
      yum:
        name: git
        state: present

    - name: install httpd
      yum:
        name: httpd
        state: present

    - name: start httpd
      service:
        name: httpd
        state: started

    - name: stop firewalld
      service:
        name: firewalld
        state: stopped

이 상황에서 test.yml 파일과 Vagrantfile의 내용도 test.yml을 사용할 수 있도록 살짝 수정해보자

 

 

# -*- mode: ruby -*-
# # vi: set ft=ruby :

###### ANSIBLE SERVER CONFIG #######

Vagrant.configure("2") do |config|
  config.vm.define "ansible-server" do |cfg|
    cfg.vm.box = "centos/7"
    cfg.vm.host_name = "ansible-server"
    cfg.vm.network "private_network", ip: "172.16.1.10"
    cfg.vm.network "forwarded_port", guest: 22, host: 60010, auto_correct: true, id: 'ssh'
    cfg.vm.provision "shell", inline: "yum -y install epel-release"
    cfg.vm.provision "shell", inline: "yum -y install ansible"
    cfg.vm.provision "file", source: "test.yml", destination: "test.yml"
    cfg.vm.provision "shell", inline: "ansible-playbook test.yml"
  end
end

하면은 vagrant 내부에서 자동으로 ansible-playbook이 실행되면서 여러가지 사항들을 설치하게 된다.

 

 

playbook와 vagrantfile에는 큰 차이가 없다. 그리고 test.yaml 파일만 수정한 상태이다. 이럴때 vagrantfile을 provision 하려고 하는데 기존과 변경사항이 없다. 하지만 test.yaml 의 변경사항까지 점검해서 그걸 감지하고 프로비저닝하겠다는 의미이다.

 

github도 이와 유사하게 index.html 파일안에 변경점이 발견되면 이를 감지하는 vcs인 .git이 존재한다.

 

예를 들어 vagrant도 위와같이 .vagrant 라는 vcs가 생긴것을 볼 수 있다.

 

이 상황에서

vagrant provision

해주면 프로비저닝된다.

배포된 인스턴스의 정보를 긁어와보자

virsh domifaddr vagrant_ansible-server

 

제대로 배포된 것을 확인할 수 있다.

 

 

그럼 지금 상황을 파악해보면 아래쪽에 192.168.122.x 의 주소가 있고 스위치 주소로(192.168.122.1)의 자체 주소가 있다.

위쪽에는 가상의 스위치가 만들어져서 172.16.1.x(스위치 주소는 172.16.1.1)의 주소를 가지고 있다.

그리고 인스턴스는 이 둘과 연결되어 .10의 주소를 가지고 있는 상태이다.

 

이걸확인하기 위해 `virsh net-list` 명령어를 사용해보면 위와같이 구성되어 있는데

virsh net-dumpxml default

virsh net-dumpxml vagrant0

를 보면

위와같이 작성되어 있다. 그래서 172.16.1.1에 연결도 가능하다.

 

나중에 가상머신을 추가로 연결한다면 virsh 스위치에 연결시키면 내부적으로 관리도 가능해질것이다.

 

이제 서버는 준비가 되었다.

그러니 추가 노드를 만들어보자

 

# -*- mode: ruby -*-
# # vi: set ft=ruby :

###### ANSIBLE NODE 1~3 ######
Vagrant.configure("2") do |config|
  config.vm.define "ansible-node1" do |cfg|
    cfg.vm.box = "centos/7"
    cfg.vm.host_name = "ansible-node1"
    cfg.vm.network "private_network", ip: "172.16.1.11"
    cfg.vm.network "forwarded_port", guest: 22, host: 60011, auto_correct: true, id: "ssh"
  end
  config.vm.define "ansible-node2" do |cfg|
    cfg.vm.box = "centos/7"
    cfg.vm.host_name = "ansible-node2"
    cfg.vm.network "private_network", ip: "172.16.1.12"
    cfg.vm.network "forwarded_port", guest: 22, host: 60012, auto_correct: true, id: "ssh"
  end
  config.vm.define "ansible-node3" do |cfg|
    cfg.vm.box = "centos/7"
    cfg.vm.host_name = "ansible-node3"
    cfg.vm.network "private_network", ip: "172.16.1.13"
    cfg.vm.network "forwarded_port", guest: 22, host: 60013, auto_correct: true, id: "ssh"
  end
end

###### ANSIBLE SERVER CONFIG #######

Vagrant.configure("2") do |config|
  config.vm.define "ansible-server" do |cfg|
    cfg.vm.box = "centos/7"
    cfg.vm.host_name = "ansible-server"
    cfg.vm.network "private_network", ip: "172.16.1.10"
    cfg.vm.network "forwarded_port", guest: 22, host: 60010, auto_correct: true, id: 'ssh'
    cfg.vm.provision "shell", inline: "yum -y install epel-release"
    cfg.vm.provision "shell", inline: "yum -y install ansible"
    cfg.vm.provision "file", source: "test.yml", destination: "test.yml"
    cfg.vm.provision "shell", inline: "ansible-playbook test.yml"
  end
end
vagrant up

ping -c 3 172.16.1.10 ; ping -c 3 172.16.1.11 ; ping -c 3 172.16.1.12 ; ping -c 3 172.16.1.13

그런데 지금 노드들에는 패스워드가 없다. 그래서 접근시 아무렇게나 들어올 수 있다는 단점이 있다.

 

먼저 ansible-server로 접속해보자

 

sudo vi /etc/ansible/hosts

하고 나갈려고 하면 안된다.

 

3-1. 실습 - 기본 Vagrant 생성하기 + Ansible 되게하기

 

ansible은 sudo의 권한은 있다. 그래서 /etc/ansible/hosts는 root의 권한을 불러와서 사용

vagrant는 sudo에 포함되어 있다. 그럼 서버가 명령을 전달할때 vagrant로 전달하더라도 상대도 vagrant로 받게 된다.

 

내가 playbook을 만들때 playbook은 노드들에게 명령을 전달하는 것인데 노드들이 실행할 수 있는 권한도 없는데 당연히 실행이 안된다. 그래서 노드들에게 루트 권한을 잠시 빌려와서 sudo로 실행하라고 할 수 있다.

 

 

vi /etc/ssh/sshd_config

이 안에서 PasswordAuthentication을 yes로 바꾸어주자.

 

그런데 모든 node들에서 이것을 허용해주어야 한다. Vagrant 파일에서 이를 해결하기 위한 내용을 추가해서 이 문제를 해결해보자. vagrant server에서는 할 필요 없다.

 

현재 server에서 node로의 접속은 불가능한 상태이다. 그러니 다음을 해보자

Vagrantfile에서 a.sh를 각 노드에 붙여넣기.

a.sh 파일은 /etc/ssh/sshd_config 파일의 내용중 PasswordAuthentication no를 PasswordAuthentication yes로 변경하고 ssh를 재실행하는 것을 포함해야한다.

 

또한, 서버에서는 각 노드들의 서버인증을 위해 keyscan 해야 한다.

 

vi auth.sh

 

를 확인해보면 위와같이 나오는 것을 볼 수 있다. 이걸 Vagrantfile안에 넣고 실행시키자

 

위와같이 2줄을 추가해주었다.

 

# -*- mode: ruby -*-
# # vi: set ft=ruby :

###### ANSIBLE NODE 1~3 ######
Vagrant.configure("2") do |config|
  config.vm.define "ansible-node1" do |cfg|
    cfg.vm.box = "centos/7"
    cfg.vm.host_name = "ansible-node1"
    cfg.vm.network "private_network", ip: "172.16.1.11"
    cfg.vm.network "forwarded_port", guest: 22, host: 60011, auto_correct: true, id: "ssh"
    cfg.vm.provision "file", source: "auth.sh", destination: "auth.sh"
    cfg.vm.provision "shell", inline: "/bin/bash auth.sh", privileged: true
  end
  config.vm.define "ansible-node2" do |cfg|
    cfg.vm.box = "centos/7"
    cfg.vm.host_name = "ansible-node2"
    cfg.vm.network "private_network", ip: "172.16.1.12"
    cfg.vm.network "forwarded_port", guest: 22, host: 60012, auto_correct: true, id: "ssh"
    cfg.vm.provision "file", source: "auth.sh", destination: "auth.sh"
    cfg.vm.provision "shell", inline: "/bin/bash auth.sh", privileged: true
  end
  config.vm.define "ansible-node3" do |cfg|
    cfg.vm.box = "centos/7"
    cfg.vm.host_name = "ansible-node3"
    cfg.vm.network "private_network", ip: "172.16.1.13"
    cfg.vm.network "forwarded_port", guest: 22, host: 60013, auto_correct: true, id: "ssh"
    cfg.vm.provision "file", source: "auth.sh", destination: "auth.sh"
    cfg.vm.provision "shell", inline: "/bin/bash auth.sh", privileged: true
  end
end

###### ANSIBLE SERVER CONFIG #######

Vagrant.configure("2") do |config|
  config.vm.define "ansible-server" do |cfg|
    cfg.vm.box = "centos/7"
    cfg.vm.host_name = "ansible-server"
    cfg.vm.network "private_network", ip: "172.16.1.10"
    cfg.vm.network "forwarded_port", guest: 22, host: 60010, auto_correct: true, id: 'ssh'
    cfg.vm.provision "shell", inline: "yum -y install epel-release"
    cfg.vm.provision "shell", inline: "yum -y install ansible"
    cfg.vm.provision "file", source: "test.yml", destination: "test.yml"
    cfg.vm.provision "shell", inline: "ansible-playbook test.yml"
    cfg.vm.provider :libvirt do |resource|
      resource.memory = 2048
      resource.cpus = 2
    end
  end
end

하는김에 libvirt로 만드는 서버에 메모리와 cpu 갯수를 올려주었다. 즉시 반영은 안되고 재부팅을 해야한다.

 

vagrant provision

vagrant ssh ansible-server

ssh-keyscan 172.16.1.11 >> ~/.ssh/known_hosts
ssh-keyscan 172.16.1.12 >> ~/.ssh/known_hosts
ssh-keyscan 172.16.1.13 >> ~/.ssh/known_hosts

ansible all -m ping -k

이제 서버는 모든 노드를 관리할 수 있는 상태가 되었다.

단, vagrant 계정으로 관리할 수 있는 상태이다.

만약 root로 관리하고 싶다면 vagrant에서 플레이북을 실행할 때 root의 권한을 일시적으로 빌려와서 쓰는 방법(become: true)

 

두번째, root 계정에서 key 만들고 퍼블릭키를 배포하여 사용하는 방법

Terraform

 

Ansible

노드 자체를 관리하고 싶다면??

내가 직접 노드에 접속할수는 없으니 master에 들어가서 배포를 하는등의 작업이 이루어지게 된다.

 

우리가 과연 master로 노드 자체를 관리할 수 있을까? 할 수 있는 방법은 존재한다. SSH - Ansible을 쓰면 되는 거다.

 

SSH 연결

1. 서버인증

접속하고자 하는 서버가 정상적인 서버인지 여부를 검증하기 위한 절차이며

접속 요청을 받은 서버는 클라이언트에게 자신의 공개키를 전송하게 된다. 만약 첫번째 접속이었다면 해당 공개키를 저장할 것인지 여부를 묻게되고 클라리언트는 yes를 타이프하면 공개키가 .ssh/known_hosts에 등록된다.

이후 재 접속을 시도하면 해당 공개키가 있는 것을 확인할 수 있으므로 서버에 대한 인증 절차를 거칠 수 있다.

 

2. 클라이언트 인증

일반적으로 두가지 방식이 있다.

- 패스워드 인증 : test123 -> ansible 에서는 -k 옵션 사용한다.

- 안전한 클라이언트 인증을 위해 key-pair를 사용한다.

 

1) 클라이언트는 key-pair를 만든다. (id_rsa[개인키], id_rsa.pub[공개키])

2) 퍼블릭키를 서버의 .ssh/authorized_keys에 등록시킨다.

3) 클라이언트는 서버에 접속하기 위해 사용할 key pair 의 ID를 서버로 전송한다.

4) 서버는 ID와 매칭되는 키가 authorized_keys에 등록되어 있는지 여부를 확인한다.

5) 만약 해당 키가 등록되어 있다면 난수(임의의 문자열)를 생성하고 이를 서버가 가지고 있는 퍼블릭키를 이용하여 암호화 시키고 이를 클라이언트에게 전달한다.

6) 클라이언트는 자신이 가지고 있는 개인키를 이용하여 이를 복호화 한다. 정상적으로 처리되면 난수가 보일것이다.

7) 이 난수를 hash 한다. 임의의 문자열이 나온다. 이를 다시 서버에게 전달한다.

8) 서버는 자신이 가지고 있던 난수를 동일한 hash function에 넣고 임의의 문자열을 확인한다. 이를 클라이언트로부터 받은 해시코드와 비교한다.

만약 동일하다면 내가 가지고 있는 퍼블릭키의 주인이라는 것을 증명한 것이다.

9) 클라이언트가 증명되었으므로 정상적인 데이터 송수신이 가능해 진다.

 

우리는 key를 이용한 방법을 쓰자

 

현재 환경에서의 문제점

1. 서버는 앤서블로부터 명령을 받아서 처리하는 CentOS VM이다.

2. 이 서버들은 현재 패스워드 인증이 지원된다. -> 서버에서 패스워드 인증을 disable 시켜야 한다.

3. 클라이언트는 접속할때 기본으로 자신의 키를 이용하여 접속하도록 설정한다.

4. 다수의 서버의 초기 접속시 퍼블릭키를 클라이언트의 known_hosts에 어떻게 등록할 것인가? 만약 없다면 접속하고자 하는 서버의 숫자만큼 'yes'를 타이프해야한다.

 

이를 해결하기 위해,  StrictHostKeyChecking=no 옵션을 이용하면 해당 서버가 등록되어 있지 않더라도 접속이 되도록 할 수 있다. 하지만 이는 보안상 절대 추천하지 않는 방법이다.

 

ssh-keyscan을 통해 서버의 퍼블릭 키를 가져와야 한다.

 

5. 키를 생성할 때 생성하는 사람에게 무엇인가를 물어보는 단계는 모두 생략되어야 한다. 즉, ssh-keygen에서 옵션을 이용하여 key-pair를 즉시 요청 생성해야 한다.

* 우리같은 관리자들은 유저가 우리에게 물어보는 단계가 모두 생략될 수 있어야 함

 

환경

 

 

1. 설치하기

1. ansible을 설치한다.

yum -y install epel-release

yum -y install ansible

vi /etc/ansible/hosts  #인벤토리 파일

인벤토리 파일은 내가 관리하고자 하는 서버가 등록되어 있는 파일이다.

 

2. 관리하고자 하는 서버들의 IP 주소를 /etc/ansible/hosts에 등록한다. 제일 아래쪽에 등록해주자.

ansible all -m ping

하면 등록되어 있는 서버들 모두에 ping을 보내는 방식이다. 문제는 이렇게하면 fingerpring 인증하라는게 돌아온다. 그래서  이건 좋은 방법이 아니다.

 

패스워드를 쓰는 것도 좋은 방법이 아니다.

 

바로 응답이 나올 수 있도록 만들어야 한다.

 

2. 설정하기

3. 관리하고자 하는 ssh 서버에서 사용자 인증을 패스워드로 하지 않도록 설정

wok에 들어가서 한개의 인스턴스의 Serial을 실행시키고

cat /etc/ssh/sshd_config | grep PasswordAuth

에서 이거 관련된 부분들을 no로 바꾸어주자

 

sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config

로 내용을 수정해주자.

 

이제부터는 공용/개인키로만 접속해야 한다.

 

 

4. 클라이언트쪽(KVM) 에서 key-pair를 생성하고 public key를 각 서버의 .ssh/authorized_keys에 등록해야 한다.

ssh-keygen -q -f ~/.ssh/id_rsa -N ''
# ssh-keygen -q -N ''
# 이렇게 깔끔하게 해주어도 id_rsa로 생긴다. -f는 파일명을 바꿀려고 하는 것이다.


ssh-copy-id vagrant@211.183.3.~
ssh-copy-id vagrant@211.183.3.~
ssh-copy-id vagrant@211.183.3.~
ssh-copy-id vagrant@211.183.3.~

파일이 생성된다. 그리고 id_rsa_pub도 만들어진다.

 

결과는

이렇게 키 두개가 생기게 된다.

 

id_rsa.pub를 각 서버에 붙여넣기 (authorized_keys) 한다. 이와 유사하게 생성된 인스턴스들에도 이 명령어를 넣어주자.

 

이 상황인데 이제 id_rsa.pub을 인스턴스들의 .ssh/authorized_keys로 붙여넣어주자.

 

그렇다보니 처음 이미지 만들때 이거 다 집어넣으면 편하다는 것이다.

 

 

5. kvm의 .ssh 아래에 있는 id_rsa.pub를 복사하여 각 서버의 .ssh 아래에 authorized_keys 파일에 붙여넣기 한다.

직접 인스턴스에 들어가서 ~/.ssh/authorized_keys에 host os의 id_rsa.pub을 복사해서 붙여넣어주자

 

그리고 권한 부여도 해주자

chmod 600 ~/.ssh/authorized_keys

 

모든 인스턴스에 반복해주자

 

6. 접속하고자 하는 서버의 public key를 끌고와서 .ssh/known_hosts 등록시킨다.

ssh-keyscan 211.183.3.129 >> ~/.ssh/known_hosts
ssh-keyscan 211.183.3.130 >> ~/.ssh/known_hosts

키들이 보인다.

ansible centos -m ping

요렇게 초록색이 나오면 성공이다. 안되면 처음부터 다시해야 한다.

 

ansible centos -m shell -a 'ip a'

shell로 ip a 라는 명령어를 보내서 ip들을 확인해볼 수 있다.

 

만약 .ssh 아래에 id_rsa 파일이 아닌 별도의 장소에 보관되어 있거나 기본 이름이 아닌경우라면 인증이 되지 않는다. 이 경우에는 다음과같은 방법을 사용할 수 있다.

 

예를들어 키 이름이 chulsoo.pem -- chulsoo.pem.pub

 

vi /etc/ssh/ssh_config

파일 가장 아래쪽으로 가서 위와같이 pem 파일 위치를 등록할 수도 있다. 모든 계정을 root로 인증을 하되 파일은 지정해놓은것이다.

 

이때 앤서블 본체는 앤서블 소프트웨어 그 자체이기 때문에 서버/클라이언트 구성과 같은 형태를 취하지 않는다.

 

3. 앤서블의 인벤토리

- 서버관리를 위한 목록

- 기본적으로 /etc/ansible/hosts를 이용한다.
- 관리의 편의 또는 구분의 편의, 효율적(hosts에만 쓰면 너무 길어짐) 인 사용을 위하여 여러 인벤토리 파일을 별도로 만들어 사용할 수 있다.

예를들어서 위와같이 -i 옵션으로 인벤토리들을 불러오고 그 중에서도 production이라는 list-host를 불러올 수 있다.

 

4. 모듈

모듈 = 함수

앤서블에서 실행된 하나하나의 명령과 같은 것

- 리눅스에서 사용하는 각각의 명령어를 별도의 모듈로 지정해둔 것으로

- 일반 shell을 이용하면 지원되지 않는 "멱등성" 을 제공하는 경우가 많다.

* 멱등성이란 어떤 특정 조건을 갖추어야 하는 상태를 만드는 것. 예를 들어 a라는 파일에 a=b라는 값을 넣었다고 가정해 보자. 만약 shell을 이용하여 동일 값을 추가한다면 추가된다. 즉 한번 했을때 결과가 이미 만족했다면 더 이상 안한다.

 

start -> started, stop -> stopped

shell 모듈은 좋지 않다. 보통 체크용도로만 쓰자. 실재로는 체크 모듈이 정말 많다. 

ansible all -m ping -k
ansible all -m shell -a "hostname" -k
ansible k8smaster -m shell -a "hostname" -k
ansible k8snode -m shell -a "sudo apt-get update -y" -k
ansible all -m shell -a "sudo touch /home/user1/test.txt" -k
# 아직 id_rsa.pub을 전체 서버에 던져주지 않은 경우
# 위와 같이 -k로 SSH 패스워드를 통해 접속해준다.

 

All modules — Ansible Documentation

 

All modules — Ansible Documentation

 

docs.ansible.com

ansible all -m shell -a "systemctl stop firewalld"

그래서 m은 모듈이다.

요런식으로 작동하게 된다.

 

[플레이북](작업 명세서)

앤서블에서 명령이라고 하면 스크립트(코드)아며 앤서블을 사용할 때 필요한 작업은 플레이북의 구현과 실행이라고 할 수 있다. 이를 간단히 yaml 작성할 경우 다수의 명령을 한꺼번에 실행할 수 있게 된다. yaml 파일로 작성되며 재사용 역시 가능하다.

 

 

- 실습

예를들어 인벤토리는 위와같이 만들어준다. lst로 끝나기만 하면 되나보다

 

먼저 파일 두개를 만들자

 

seoul.lst 안에는 위와같이 ip를 적어주자 ip는 내가 만든 가상머신 ip이다.

그리고 seoul.lst내용을 busan.lst에 넣어놓자

실재로 ping 모듈로 해보니 이렇게 된다.

삼성만 하려면 이렇게 하면된다.

 

설치도 되기는 하지만 이렇게 쓰면 멱등성에 문제가 생김으로 가급적 다른 모듈을 써주자

 

 

- user 모듈

사용자를 생성, 삭제, 수정할 수 이는 모듈

ansible all -m user -a "name=newuser1"

이 모듈은 생성시 state:present를 유지해 달라는 의미이다. 만약에 빈자리이다면? 없는 상태로 유지해달라고 해보자

ansible all -m user -a "name=user2 state=absent"

state=absent는 이거 말고도 yum이라는 모듈에서도 사용이 가능하다.

ansible all -m yum -a "name=httpd state=absent"

만약에 한번더 입력하더라도 멱등성 유지를 위해 상태변화가 생기지 않는다.

ansible all -m yum -a "name=httpd state=present"

다시 설치하고자 한다면 present로 state를 변경해주면 된다.

 

- copy 모듈

copy는 로컬에서 작성한 파일을 노드에 복사할 수 있는 모듈이다. 예를들어 httpd가 설치된 각 노드에 로컬서버에서 작성한 index.html 파일을 복사해보자

curl https://www.naver.com > index.html

ansible all -m copy -a "src=index.html dest=/var/www/html/index.html"

ansible all -m service -a "name=firewalld state=stopped"

ansible all -m service -a "name=httpd state=started"

사이트가 변경된것을 확인할 수 있다.

 

5. 플레이북

플레이북은 yaml 형식으로 작성된 시스템 정의서 같은 것

위와같은 구조로 작성해주면 된다.

--- : yaml 형식의 파일임을 알려주는 선언

name: 로그에 표시되는 이름

hosts: 배포대상

tasks: 실제 작업

배포에 걸리는 시간을 줄이는데 gather_facts를 false 또는 no라고 해놓자.

 

gather_facts는 각각의 노드 정보를 수집하는데 수집해서 무언가를 하겠다는 의미이지만 no로 하면 수집안하겠다. 즉 네 os 환경에 상과없이 처리하라는 의미이자. 내가 대상의 정보를 아니 그냥 처리하겠다는 것이다.

 

수집되는 정보는 도대체 무엇이 있을까?

ansible all -m setup -k >gatherfacts.txt

하면 위와같이 엄청 많은 정보들이 불러와진다.

 

이 중에서도 우리에게 필요한 정보를 긁어와보자

```

cat gatherfacts.txt | grep SSH_CONNECTION

```

하면 위와같이 연결정보들을 모두 확인할 수 있다.

 

```

cat gatherfacts.txt | grep ansible_eth0

cat gatherfacts.txt | grep ansible_eth1

cat gatherfacts.txt | grep ansible_pkg_mgr  # 패키지 매니저

cat gatherfacts.txt | grep ansible_distribution

```

보면 ansible_distribution으로 OS 정보가 뜬다. 이걸 이용해서 OS 별로 조건을 걸수도 있다.

 

그런데 이런정보까지 다 필요없이 그냥 해달라 그래서 gather_facts no라고 해놓는것이다.

 

물론 우리가 쓰는 OS가 centos이면 플레이북의 yum은 오류가 나지만... 이것도 조건을 걸어주어서 해결할 수 있다.

 

실습

- 플레이 내에서 태스크 구현과 엔진엑스의 배포

1. 관리자 권한으로 실행

이거 실행할때만 관리자 권한을 쓰라고 할 수 있다.

 

2. SELinux 설정

SELinux를 파이썬에서 사용할 수 있도록 libselinux-python을 yum으로 설치하자.

=을 쓰는 방법으로도 가능하다. 하지만 잘 안쓴다.

 

 

3. EPEL 저장소 설치

일반적으로 nginx는 yum으로 설치되지 않는다. epel-release 설치가 된 후에 설치가능해진다.

```

# 반드시 ansible-server에서 실행하고 만들자

vi httpdinstall.yaml

```

---
- name: httpd installation
  hosts: all
  gather_facts: no

  tasks:
    - name: DIRECTORY CREATION
      file:
        path: /home/vagrant/testdir
        state: directory
    - name: libselinux-python install
      become: yes
      yum:
        name: libselinux-python
        state: present
    - name: httpd install
      become: yes
      yum:
        name: httpd
        state: present
    - name: start httpd
      become: yes
      service:
        name: httpd
        state: started
        enabled: true
    - name: stop firewalld
      become: yes
      service:
        name: firewalld
        state: stopped
        enabled: false

```

ansible-playbook httpdinstall.yaml -k

```

성공한것을 확인할 수 있다.

curl로 ip를 따보니 httpd 의 정보가 보인다.

 

quiz. get_url을 이용하여 https://www.nginx.com 의 페이지를 각 노드의 /var/www/html에 붙여 넣기 하세요.

---
- name: httpd installation
  hosts: all
  gather_facts: no

  tasks:
    - name: DIRECTORY CREATION
      file:
        path: /home/vagrant/testdir
        state: directory

    - name: libselinux-python install
      become: yes
      yum:
        name: libselinux-python
        state: present

    - name: httpd install
      become: yes
      yum:
        name: httpd
        state: present

# 이 부분이 추가되었다.
    - name: index creation
      become: yes
      get_url: url=https://www.nginx.com dest=/var/www/html/index.html

    - name: start httpd
      become: yes
      service:
        name: httpd
        state: started
        enabled: true

    - name: stop firewalld
      become: yes
      service:
        name: firewalld
        state: stopped
        enabled: false

```

ansible-playbook httpdinstall.yaml -k

```

 

해주면 nginx.com 홉페이지가 curl로 긁어와진다.

---
- name: using sed
  hosts: localhost
  become: yes
  gather_facts: no

  tasks:
    - name: change selinux
      replace:
        path: /etc/selinux/config
        regexp: 'SELINUX=enforcing'
        replace: 'SELINUX=disabled'

```

vi sed.yaml

ansible-playbook sed.yaml -k

```

파일을 위와같이 작성하여 모든 노드의 selinux=disabled 로 바꾸어주자.

---
- name: httpd installation
  hosts: all
  gather_facts: no

  tasks:
    - name: SELINUX disabled
      become: yes
      replace:
        path: /etc/selinux/config
        regexp: 'SELINUX=enforcing'
        replace: 'SELINUX=disabled'
    - name: DIRECTORY CREATION
      file:
        path: /home/vagrant/testdir
        state: directory

    - name: libselinux-python install
      become: yes
      yum:
        name: libselinux-python
        state: present

    - name: httpd install
      become: yes
      yum:
        name: httpd
        state: present

    - name: index.html creation
      become: yes
      get_url: url=https://www.nginx.com dest=/var/www/html/index.htm

    - name: start httpd
      become: yes
      service:
        name: httpd
        state: started
        enabled: true

    - name: stop firewalld
      become: yes
      service:
        name: firewalld
        state: stopped
        enabled: false

 

```

ansible-playbook httpdinstall.yaml -k

```

 

---

 

이번에는

```

cp sed.yaml file.yaml

vi file.yaml

```

---
- name: using sed
  hosts: localhost
  become: yes
  gather_facts: no

  tasks:
    - name: line in file
      lineinfile:
        path: /etc/ansible/hosts
        line: 172.16.1.14

위와 같이 내용을 넣고 실행해보자

그러면 가장 아래쪽에 ip가 추가 등록되어 있는 것을 확인할 수 있다.

 

참고로 멱등성이 적용되므로 한번더 실행한다고 같은게 또 등록되지는 않는다.

 

---
- name: using sed
  hosts: localhost
  become: yes
  gather_facts: no

  tasks:
    - name: line in file
      lineinfile:
        path: /etc/ansible/hosts
        line: "{{ item }}"
      with_items:
        - "172.16.1.15"
        - "172.16.1.16"

file.yaml 을 한번더 수정해보자

 

위와같이 된다.

---

block 단위로 입력

여러줄을 입력할 수도 있고 블럭 형태로 그대로 도장찍듯이. 띄어쓰기 등도 그대로 옮겨진다.

 

---
- name: using sed
  hosts: localhost
  become: yes
  gather_facts: no

  tasks:
    - name: line in file
      blockinfile:
        path: /etc/ansible/hosts
        block: |

          [centos]
          172.16.1.11
          172.16.1.12
          172.16.1.13

 

요런식으로 들어오게 된다.

 

파이프를 쓰게 되면 마지막 라인을 비우지 않는다.

그리고 # BEGIN ANSIBLE MANAGED BLOCK 부분이 BLOCK임을 알려준다.

 

```

ansible centos -m ping -k --list-hosts

```

 

 

 

---

 

nfs ansible playbook 만들기

---
- name: nfs-server
  hosts: localhost
  gather_facts: no
  tasks:
    - name: DIR creation
      file:
        path: /home/vagrant/shared
        state: directory
        mode: 0777
    - name: installation nfs server
      become: yes
      yum:
        name: nfs-utils
        state: present
    - name: configuring /etc/exports
      become: yes
      lineinfile:
        path: /etc/exports
        line: /home/vagrant/shared 172.16.1.0/24(rw,sync)
    - name: start nfs server
      become: yes
      service:
        name: nfs-server
        state: restarted

#- name: nfs-client
#  hosts: centos
#  gather_facts: no
#  tasks:

nfsconfiguration.yaml 을 만들어서 배포해보자

```

ansible-playbook nfsconfiguration.yaml -k

```

728x90