Windows 빌드 서버 초기 구성을 위한 Ansible 설계
이전에 테스트한 방법을 기반으로 윈도우 11을 처음 설치한 상태에서 빌드 서버로 세팅하기 위한
Ansible 초기 구성 및 설계를 하나씩 진행해보았다.
Windows 테스트 환경 구성
VM 다운로드 및 구동
Hyper-V에서는 Windows Dev Environment(개발환경) 이라는 가상환경을 90일간 평가판으로 제공한다.
Hyper-V의 Quick Create(빨리 만들기)를 통해서 생성할 수 있으며, Visual Studio 2022 Community Edition을 비롯한 여러가지 패키지를 내장하고 있다.
The evaluation virtual machine includes:
- Windows 11 Enterprise (Evaluation)
- Visual Studio 2022 Community Edition with UWP, .NET Desktop, Azure, and Windows App SDK for C# workloads enabled
- Windows Subsystem for Linux 2 enabled with Ubuntu installed
- Windows Terminal installed
- Developer mode enabled
다만, License Key를 활성화할 수 없고 90일 뒤에는 만료되어서 더 사용하려면 새로 Windows Dev Environment를 구성해야한다.
만약 Hyper-V가 아닌 다른 가상화 도구를 통해서 해당 Windows Dev Environment를 사용하고 싶다면 아래의 링크에서 각 도구에 맞는 가상화 파일을 다운로드 받아서 추가하면 된다.
https://developer.microsoft.com/en-us/windows/downloads/virtual-machines/
개발 환경으로 제공된 VM은 user 계정으로 기본 로그인되며, 패스워드가 존재하지 않는다.
Ansible로 관리하기 위해서 아래의 세팅을 powershell 실행 후 수동으로 진행해 준다.
OpenSSH 서버 설정
# Powershell 관리자 권한 확인
(New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
# False일 경우 관리자 권한으로 powershell 실행
Start-Process powershell -Verb RunAs
# openssh 설치 확인
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'
Name : OpenSSH.Client~~~~0.0.1.0
State : Installed
Name : OpenSSH.Server~~~~0.0.1.0
State : NotPresent <- openssh server는 미설치
# openssh server 설치
# openssh client도 필요 시 설치
# Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Path :
Online : True
RestartNeeded : False
# ssh 서비스 실행 및 재기동 시 자동시작 설정
Start-Service sshd
Set-Service -Name sshd -StartupType 'Automatic'
# 22번 Port 방화벽 허용 설정
if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) {
Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..."
New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
} else {
Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists."
}
기본 계정(User)에 패스워드 설정
Set-LocalUser -Name 'user' -Password (ConvertTo-SecureString -AsPlainText <password> -Force)
Ansible 관리 서버에서 SSH 접속 테스트
root@ersia:~/ansible# ssh user@192.168.0.18
The authenticity of host '192.168.0.18 (192.168.0.18)' can't be established.
...
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
...
user@192.168.0.18's password:
Microsoft Windows [Version 10.0.22621.3007]
(c) Microsoft Corporation. All rights reserved.
user@WINDEV2401EVAL C:\Users\User>exit
Connection to 192.168.0.18 closed.
Ansible Role을 통한 초기 설계 및 구성
yml 파일 한개에 Ansible을 통해 수행할 내용을 모두 작성하는 것도 가능하지만,
다양한 Module과 변수를 사용하면 내용이 점점 복잡해지고 중복 코드가 발생하게 된다.
복잡한 코드를 분리해 관리하고 재사용 가능하도록 Role을 통해 구성하도록 한다.
Ansible Project 디렉토리 생성 및 기본 설정
# Windows Build Server Project 디렉토리 생성
mkdir windows_build_server
cd windows_build_server
# ansible.cfg 파일 생성
cat <<EOT > ansible.cfg
[defaults]
inventory = ./inventory.ini
roles_path = ./roles
ask_pass = yes
EOT
# inventory.ini 파일 생성
cat <<EOT > inventory.ini
[windows]
build_server1 ansible_host=192.168.0.18
[windows:vars]
remote_tmp = C:\Users\user\.ansible\tmp
become_method = runas
ansible_connection=ssh
ansible_user=user
ansible_shell_type = cmd
EOT
# 초기화 작업을 구성할 role 생성
ansible-galaxy role init --init-path ./roles build_initialize
# role에서 사용할 변수 선언
cat <<EOT >> roles/build_initialize/vars/main.yml
ansible_connection: ssh
ansible_port: 22
ansible_shell_type: cmd
EOT
# role에서 수행할 task 작성
cat <<EOT >> roles/build_initialize/tasks/main.yml
- include_vars: vars/main.yml
- name: test powershell
win_shell: |
get-host
register: result_get_host
- name: display result_get_host
debug:
var: result_get_host
EOT
# ansible을 수행할 play-book 작성
cat <<EOT >> windows_build_server.yml
---
- name: Configuration Build Server on Windows
hosts: windows
tasks:
- name: build_initialize role
import_role:
name: build_initialize
EOT
수행한 명령어와 생성된 파일 중 이전에 사용하지 않았던 것을 좀 더 상세히 살펴보면 아래와 같다.
Ansible.cfg 파일
ansible 명령을 수행할 때 필요한 환경설정 내용을 담고 있는 파일이다.
기본적으로 /etc/ansible/ansible.cfg 파일을 읽고 있으며, 아래의 우선순위에 따라서 가장 먼저 발견되는 파일을 사용한다.
- ANSIBLE_CONFIG : 환경변수로 설정된 파일
- ./ansible.cfg : ansible 명령을 수행한 현재 디렉토리 내의 파일
- ~/.ansible.cfg : ansible 명령을 수행한 계정의 홈 디렉토리의 hidden 파일
- /etc/ansible/ansible.cfg : ansible 설치 시 생성되는 default 파일
사용되는 파일이 어떤 것인지 알고 싶다면 --version 옵션을 추가해서 ansible 명령어를 수행하면 확인할 수 있다.
# ansible.cfg 파일이 있는 디렉토리에서 명령어 수행 시 config 파일 위치
root@ersia:~/ansible/windows_build_server# ansible --version
ansible [core 2.15.8]
config file = /root/ansible/windows_build_server/ansible.cfg
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
...
libyaml = True
# ansible.cfg 파일이 없는 디렉토리에서 명령어 수행 시 config 파일 위치
root@ersia:~/ansible# ansible --version
ansible [core 2.15.8]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
...
libyaml = True
프로젝트 디렉토리를 별도로 생성했으므로 프로젝트 디렉토리 최상위에 ansible.cfg 파일을 생성했다.
Inventory.ini 파일
ansible.cfg 파일에 명시된 inventory 파일을 사용하며, 별도의 설정이 없다면 /etc/ansible/hosts 파일을 기본으로 사용한다.
inventory 파일은 INI와 YAML 두가지 형식을 지원한다.
# INI 형식
root@ersia:~/ansible/windows_build_server# cat inventory.ini
[windows]
build_server1 ansible_host=192.168.0.18
[windows:vars]
remote_tmp = C:\Users\user\.ansible\tmp
become_method = runas
ansible_connection=ssh
ansible_user=user
ansible_shell_type = cmd
# YAML 형식
root@ersia:~/ansible/windows_build_server# cat inventory.yml
windows:
hosts:
build_server1:
ansible_host: 192.168.0.18
vars:
remote_tmp: C:\Users\user\.ansible\tmp
become_method: runas
ansible_connection: ssh
ansible_user: user
ansible_shell_type: cmd
inventory 파일도 프로젝트 디렉토리를 별도 생성하였으므로 프로젝트 디렉토리 최상위에 inventory.ini 파일을 생성하고 ansible.cfg파일에 경로를 작성해주었다.
Roles 디렉토리 및 Role Template
Ansible Galaxy를 통해 Role을 install 했던 것처럼 별도의 Role을 만들어 모듈화해 관리할 수 있다.
ansible-galaxy role init 명령을 통해 기본 role template을 생성할 수 있으며 기본 구조는 아래와 같다.
root@ersia:~/ansible/windows_build_server# tree roles/
roles/ # role을 모아두는 최상위 디렉토리, 별도의 role을 install할 수도 있다.
└── build_initialize # 생성한 role 이름
├── defaults # role 변수의 기본값을 정의하는 디렉토리 (우선순위가 가장 낮다)
│ └── main.yml
├── files # role에서 참조하는 정적파일을 모아두는 디렉토리
├── handlers # role 핸들러의 정의를 작성하는 디렉토리
│ └── main.yml
├── meta # role의 작성자, license 등 메타 정보를 작성하는 디렉토리
│ └── main.yml
├── README.md # role에 대한 readme 작성파일, 사용법이나 도움말을 작성
├── tasks # role이 실제 동작하는 task 정의 내용을 작성하는 디렉토리
│ └── main.yml
├── templates # role에서 참조하는 jinja2 템플릿을 작성하는 디렉토리
├── tests # role을 테스트하는데 사용하는 inventory와 play-book을 모아두는 디렉토리
│ ├── inventory
│ └── test.yml
└── vars # role에 대한 변수값을 정의하는 디렉토리(role 내부에서 사용하는 변수)
└── main.yml
role template 디렉토리는 여러 디렉토리가 있지만 모두 존재할 필요는 없고, 사용에 필요한 디렉토리만 있어도 동작한다.
예를들어, tasks를 수행하는데 별도의 정적 파일을 사용하지 않아 files디렉토리가 필요하지 않다면 해당 디렉토리는 삭제해도 정상 동작한다. 따라서 사용하지 않는 디렉토리와 파일은 삭제하는게 유지보수하기 더 유용하다.
또한 role template 구조는 지정되어있기 때문에 임의로 디렉토리 이름이나 main.yml 파일 이름을 변경하면 제대로 동작하지 않는 경우가 발생한다.
(추가) Role에서 defaults와 vars의 차이점
defaults와 vars의 차이를 이해하려면 ansible에서의 변수값 우선순위를 이해해야 한다.
- command line values (for example, -u my_user, these are not variables)
- role defaults (defined in role/defaults/main.yml) 1
- inventory file or script group vars 2
... - play vars
... - role vars (defined in role/vars/main.yml)
... - include params
- extra vars (for example, -e "user=my_user")(always win precedence)
아래에 있는 extra vars가 가장 우선순위가 높고 맨 위에 있는 변수 타입일 수록 우선순위가 낮다.
Ansible Project 디렉토리 구조
현재까지 생성된 Ansible Project 디렉토리 구조는 다음과 같다.
root@ersia:~/ansible# tree windows_build_server/
windows_build_server/
├── ansible.cfg
├── inventory.ini
├── roles
│ └── build_initialize
│ ├── defaults
│ │ └── main.yml
│ ├── files
│ ├── handlers
│ │ └── main.yml
│ ├── meta
│ │ └── main.yml
│ ├── README.md
│ ├── tasks
│ │ └── main.yml
│ ├── templates
│ ├── tests
│ │ ├── inventory
│ │ └── test.yml
│ └── vars
│ └── main.yml
└── windows_build_server.yml
windows_build_server 디렉토리에서 ansible playbook을 수행하면 기존 powershell 명령어 테스트에 사용한 ansible task를 똑같이 수행할 수 있다.
기존에 하나의 yaml 파일로 수행하던 것에 비해서는 파일이 많아졌지만, 이런식으로 세분화 해두면 더 다양하고 확장성있는 모듈(role)을 만들 수 있다.
root@ersia:~/ansible/windows_build_server# ansible-playbook windows_build_server.yml
SSH password:
PLAY [Configuration Build Server on Windows] ***************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [build_server1]
TASK [build_initialize : include_vars] *********************************************************************************
ok: [build_server1]
TASK [build_initialize : test powershell] ******************************************************************************
changed: [build_server1]
TASK [build_initialize : display result_get_host] **********************************************************************
ok: [build_server1] => {
"result_get_host": {
"changed": true,
"cmd": "get-host",
"delta": "0:00:01.453187",
...
...
"CurrentCulture : en-US",
"CurrentUICulture : en-US",
"PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy",
"DebuggerEnabled : True",
"IsRunspacePushed : False",
"Runspace : System.Management.Automation.Runspaces.LocalRunspace",
"",
"",
""
]
}
}
PLAY RECAP *************************************************************************************************************
build_server1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
root@ersia:~/ansible/windows_build_server#
참고자료
- 15 Ansible Playbooks Example for Windows Administration : https://www.devopsschool.com/blog/15-ansible-playbooks-example-for-windows-administration/
- ansible-galaxy role : https://docs.ansible.com/ansible/latest/cli/ansible-galaxy.html#role