[{"categories":["컨테이너 기술","DevOps"],"content":"Harbor 액티브-스탠바이 배포 환경 준비 운영체제: CentOS 8.4 64bit\nHarbor 버전: harbor-offline-installer-v2.11.1.tgz\nDocker Compose: docker-compose-linux-x86_64\n역할 호스트명 IP 주소 주 Harbor Harbor01 192.168.8.161 스탠바이 Harbor Harbor02 192.168.8.162 Docker 호스트 web01 192.168.8.188 Harbor는 VMware의 오픈소스 엔터프라이즈 Docker 레지스트리 프로젝트로, 공식 Docker Registry를 기반으로 관리 UI, 역할 기반 접근 제어(RBAC), AD/LDAP 통합, 감사 로그 등의 기업 기능을 추가했습니다. Harbor의 각 컴포넌트는 Docker 컨테이너로 구축되며, Docker Compose를 사용하여 배포됩니다.\n파트 1: 주 Harbor 배포 (HTTP 모드, 인증서 없음) 이 방법은 내부 네트워크에 적합하며, SSL 인증서 없이 HTTP로 직접 Harbor에 접근합니다.\n1. Docker 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 # 기존 yum 저장소 파일 삭제 rm -rf /etc/yum.repos.d/* # Alibaba Cloud 미러에서 yum 저장소 파일 다운로드 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo # Docker CE 설치 yum install -y yum-utils yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # Docker 시작 및 부팅 시 자동 시작 설정 systemctl enable docker.service --now 2. Docker 미러 설정 1 vim /etc/docker/daemon.json 다음 내용 입력:\n1 2 3 4 5 6 { \u0026#34;registry-mirrors\u0026#34;: [ \u0026#34;https://37564ea7e7cc4824a5049fd666807c27.mirror.swr.myhuaweicloud.com\u0026#34; ], \u0026#34;insecure-registries\u0026#34;: [\u0026#34;http://192.168.8.161\u0026#34;] } 1 2 systemctl daemon-reload systemctl restart docker.service 3. Docker Compose 및 Harbor 설치 1 2 3 4 5 6 7 8 9 10 # docker-compose 및 harbor 오프라인 패키지를 /data에 배치 ls /data # docker-compose-linux-x86_64 harbor-offline-installer-v2.11.1.tgz # docker-compose 설치 cp /data/docker-compose-linux-x86_64 /usr/local/bin/docker-compose chmod a+x /usr/local/bin/docker-compose # harbor 압축 해제 tar -zxvf /data/harbor-offline-installer-v2.11.1.tgz -C /usr/local/ 4. Harbor 설정 수정 1 2 3 cd /usr/local/harbor/ cp harbor.yml.tmpl harbor.yml vim harbor.yml 다음 핵심 설정 수정:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 hostname: 192.168.8.161 http: port: 80 # https 섹션 전체 주석 처리 (HTTP 모드에서는 인증서 불필요) #https: # port: 443 # certificate: /your/certificate/path # private_key: /your/private/key/path harbor_admin_password: Harbor12345 data_volume: /images 나머지 설정은 기본값으로 유지합니다.\n5. 설치 실행 1 ./install.sh 설치 후 브라우저에서 http://192.168.8.161에 접속하여 admin / Harbor12345로 로그인합니다.\n6. 사용자 및 프로젝트 생성 Harbor Web UI에서 user1 생성:\nuser1로 로그인:\nuser1로 새 프로젝트(예: openssh) 생성:\n7. 이미지 푸시 및 풀 테스트 Docker 호스트(web01)에서 작업:\n1 2 3 4 5 6 7 8 9 10 11 # Harbor 로그인 docker login 192.168.8.161 Username: user1 Password: # Login Succeeded # 이미지 태깅 docker tag nginx:latest 192.168.8.161/openssh/nginx:latest # Harbor에 푸시 docker push 192.168.8.161/openssh/nginx:latest 성공적으로 푸시된 후 Harbor에서 이미지 확인:\n1 2 3 # 로컬 이미지 삭제 후 다시 풀 docker image rm -f 192.168.8.161/openssh/nginx:latest docker pull 192.168.8.161/openssh/nginx:latest 풀 후 Harbor에 다운로드 횟수 +1 표시:\n파트 2: 스탠바이 Harbor 배포 스탠바이 배포는 주 Harbor와 동일하며, hostname만 스탠바이 IP로 변경합니다.\n1 vim /usr/local/harbor/harbor.yml 1 hostname: 192.168.8.162 배포 후 http://192.168.8.162에 접속하여 user2 생성:\n스탠바이 Harbor에 user2로 로그인:\n파트 3: 액티브-스탠바이 복제 설정 1. 주 Harbor에 복제 대상 생성 주 Harbor에 admin으로 로그인하여 관리 → 레지스트리로 이동하고 새 대상 생성:\n스탠바이 정보 입력:\n엔드포인트 URL: http://192.168.8.162 접근 ID / 접근 키: user2 자격 증명 생성 성공:\n2. 복제 정책 생성 관리 → 복제로 이동하여 새 복제 정책 생성:\n대상 레지스트리: Harbor02 선택 모드: 이벤트 기반 (또는 다른 모드) 3. 동기화 결과 확인 Docker 호스트에서 더 많은 이미지를 주 Harbor에 푸시:\n1 2 3 4 5 docker tag httpd:2.4 192.168.8.161/openssh/httpd:2.4 docker push 192.168.8.161/openssh/httpd:2.4 docker tag mysql:5.7 192.168.8.161/openssh/mysql:5.7 docker push 192.168.8.161/openssh/mysql:5.7 주 Harbor 이미지 확인:\n스탠바이 이미지 확인 (자동 동기화됨):\n스탠바이에 주 Harbor에서 동기화된 이미지가 표시되면 액티브-스탠바이 복제가 성공한 것입니다.\n참고: 복제 설정 전에 이미 존재했던 이미지는 수동으로 동기화해야 합니다.\n파트 4: HTTPS 활성화 (자체 서명 인증서) 프로덕션 환경이나 공인 네트워크 사용 시 HTTPS를 권장합니다. 다음은 자체 서명 인증서의 전체 워크플로우입니다.\n1. CA 및 서버 인증서 생성 Harbor 서버에서 실행:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 mkdir -p /data/cert \u0026amp;\u0026amp; cd /data/cert # CA 개인키 생성 openssl genrsa -out ca.key 4096 # CA 인증서 생성 (10년 유효) openssl req -x509 -new -nodes -sha512 -days 3650 \\ -subj \u0026#34;/C=CN/CN=HarborRootCA\u0026#34; \\ -key ca.key \\ -out ca.crt # 서버 개인키 생성 openssl genrsa -out harbor.key 4096 # 서버 인증서 요청 생성 (CN = Harbor IP 또는 도메인) openssl req -sha512 -new \\ -subj \u0026#34;/C=CN/CN=192.168.8.161\u0026#34; \\ -key harbor.key \\ -out harbor.csr 2. 인증서 확장 정보 설정 SAN(Subject Alternative Name)이 포함된 v3.ext 파일 생성:\n1 2 3 4 5 6 7 8 9 10 cat \u0026gt; v3.ext \u0026lt;\u0026lt;-EOF authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] IP.1=192.168.8.161 EOF IP.1은 Harbor의 실제 접속 주소와 일치해야 합니다. 그렇지 않으면 Docker 클라이언트가 인증서 도메인 불일치 오류를 보고합니다.\n3. 서버 인증서 서명 1 2 3 4 5 openssl x509 -req -sha512 -days 3650 \\ -extfile v3.ext \\ -CA ca.crt -CAkey ca.key -CAcreateserial \\ -in harbor.csr \\ -out harbor.crt 4. Harbor 설정에서 HTTPS 활성화 1 vim /usr/local/harbor/harbor.yml https 섹션의 주석을 해제하고 인증서 경로 업데이트:\n1 2 3 4 5 6 hostname: 192.168.8.161 https: port: 443 certificate: /data/cert/harbor.crt private_key: /data/cert/harbor.key hostname은 v3.ext의 IP.1과 일치해야 합니다.\n5. Harbor 재배포 1 2 3 4 cd /usr/local/harbor/ docker-compose down ./prepare docker-compose up -d 주의: docker-compose down -v를 사용하지 마세요. -v는 네임드 볼륨을 삭제하여 Harbor 데이터베이스와 사용자 데이터가 손실됩니다.\n배포 후 https://192.168.8.161로 접속합니다.\n6. Docker 클라이언트 인증서 신뢰 설정 이미지를 푸시/풀해야 하는 모든 머신에서 실행:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 인증서 디렉토리 생성 (이름은 Harbor IP 또는 도메인이어야 함) mkdir -p /etc/docker/certs.d/192.168.8.161 # CA 인증서를 해당 디렉토리에 복사 # 방법 1: Harbor 서버에서 scp scp root@192.168.8.161:/data/cert/ca.crt /etc/docker/certs.d/192.168.8.161/ # 방법 2: 수동으로 ca.crt 파일 내용 복사 # Docker 재시작 systemctl restart docker # 로그인 테스트 docker login 192.168.8.161 # admin과 비밀번호 입력, \u0026#34;Login Succeeded\u0026#34; 표시되면 성공 파트 5: FAQ Q: HTTP 모드와 HTTPS 모드를 전환할 수 있나요?\n네. harbor.yml의 https 섹션을 수정한 후 ./install.sh를 다시 실행하면 됩니다. HTTP에서 HTTPS로 전환하려면 인증서를 먼저 생성해야 하고, HTTPS에서 HTTP로 전환하려면 https 섹션을 주석 처리하면 됩니다.\nQ: Docker 클라이언트가 HTTPS Harbor 로그인 시 \u0026ldquo;x509: certificate relies on legacy Common Name field\u0026rdquo; 오류를 보고할 때\n이는 인증서에 SAN(Subject Alternative Name)이 없기 때문입니다. v3.ext에 IP.1=\u0026lt;Harbor IP\u0026gt;가 포함되어 있고 -extfile v3.ext로 서명했는지 확인하세요.\nQ: 복제 실패를 어떻게 해결하나요?\n주와 스탠바이 간의 네트워크 연결, 대상 레지스트리 자격 증명, 해당 사용자의 대상 프로젝트 쓰기 권한을 확인하세요.\n","date":"2026-08-04","description":"","objectID":"/ko/posts/harbor-master-slave/","readingTime":5,"tags":["Harbor","Docker","이미지 레지스트리","복제","HTTPS"],"title":"Harbor 액티브-스탠바이 배포: 설치부터 HTTPS 및 복제까지","url":"/ko/posts/harbor-master-slave/","wordCount":1007},{"categories":["OpenStack"],"content":"1. OpenStack 핵심 컴포넌트 및 역할 1. HORIZON — 통합 웹 대시보드 서비스 핵심 기능: 클라우드 관리자와 일반 테넌트가 클라우드 리소스를 시각적으로 할당·모니터링할 수 있는 Web 기반 Dashboard 그래픽 관리 콘솔을 제공합니다. 2. NOVA — 컴퓨트 리소스 관리 서비스 핵심 기능: 클라우드 환경의 가상화 컴퓨트 리소스(nova-compute)를 관리·설정합니다. 역할 분담: Controller 제어 노드: nova-api, nova-scheduler, nova-conductor 등의 스케줄링 서비스를 실행합니다. 사용자가 클라우드 인스턴스 생성을 요청하면 nova-api가 요청을 수신·파싱하고, nova-scheduler가 정책에 따라 최적의 컴퓨트 노드를 선택해 부하를 스케줄링합니다. Compute 컴퓨트 노드: 클라우드 인스턴스를 실제로 호스팅하는 워커 노드(KVM, VMware ESXi, Xen 등 이기종 하이퍼바이저 지원). 제어 노드의 고가용성과 성능 병목: 제어 노드는 모든 API 게이트웨이, 데이터베이스, 메시지 큐를 담당하여 고가용성·성능 부하가 발생한다. 3. GLANCE — 이미지 서비스 핵심 기능: OS 이미지의 발견, 등록, 검색 및 스토리지 관리를 담당합니다. 일반적인 디스크 이미지 형식: KVM 일반 형식: qcow2(씬 프로비저닝 및 Copy-on-Write, COW 지원); VMware 일반 형식: vmdk. 배포 권장: 일반적으로 고가용성을 위해 제어 노드에 통합·배치하여 배포합니다. 4. SWIFT — 오브젝트 스토리지 서비스 핵심 기능: 고가용·분산·최종 일관성 객체 스토리지 서비스(Object Storage)를 제공하며, 정적 비정형 데이터, 클라우드 인스턴스 시스템 이미지, 전체 데이터 백업 등에 특화되어 있다. 5. NEUTRON — 소프트웨어 정의 네트워크(SDN) 핵심 기능: 클라우드 환경에서 네트워크 서비스(NaaS)를 제공하며 가상 라우터, 스위치, 보안 그룹, 플로팅 IP, 노드 간 트래픽 연결을 관리합니다. 네트워크 기술: VLAN, VXLAN, Geneve 및 분산 가상 라우팅(DVR)을 완전 지원; 하드웨어 SDN 모드: 프로덕션 환경에서는 보통 2대의 전용 네트워크 노드를 배치해 액티브-액티브 또는 액티브-스탠바이 클러스터를 구성한다. 6. CINDER — 블록 스토리지 서비스 핵심 기능: 클라우드 인스턴스용 영구 블록 스토리지(클라우드 디스크)를 제공하며 동적 연결·분리·스냅샷·확장이 가능하다. 백엔드 연동 능력: 기존 스토리지 어레이 연동: 화웨이 OceanStor, IBM, EMC 등 하이엔드 하드웨어 스토리지; 오픈소스 분산 스토리지 연동: Ceph RBD, GlusterFS 등. 백엔드 메커니즘: 사용자가 클라우드 디스크를 신청하면 Cinder는 지시를 백엔드 스토리지 시스템으로 하위 전달하며, 본질적으로 물리/분산 스토리지 내에 LUN을 할당·매핑한다. 비교 설명: 객체 스토리지(예: 바이두 왕판, Swift, S3)는 블록 디바이스로 직접 포맷·마운트하여 클라우드 인스턴스 파일 시스템으로 쓸 수 없지만, Cinder 블록 디바이스는 직접 포맷해 마운트해 사용할 수 있다. 7. HEAT — 오케스트레이션 서비스 핵심 기능: 선언형 템플릿(HOT 템플릿 또는 AWS CloudFormation 형식)을 기반으로 그래픽 오케스트레이션이나 스크립트를 통해 다수의 VM, 데이터베이스 클러스터, 네트워크 토폴로지, 모니터링 정책을 원클릭으로 자동 일괄 배포한다. 8. CEILOMETER — 계측 모니터링 서비스 핵심 기능: 물리·가상 리소스의 성능 지표, 가동 시간, 사용량을 수집한다. 퍼블릭 클라우드 시나리오: API 호출량, 네트워크 송출량, 컴퓨트 시간을 기록하고 과금 시스템과 연동; 프라이빗 클라우드 시나리오: 테넌트 할당량 수준을 모니터링하고 리소스 과다 사용·유휴를 식별해 용량 계획을 지원한다. 9. KEYSTONE — 통합 인증 및 권한 부여 서비스 핵심 기능: OpenStack 전체 보안의 초석으로, 플랫폼 전반의 테넌트(Project), 사용자(User), 역할(Role), 서비스 카탈로그, 토큰 인증을 담당한다. 엔터프라이즈 아이덴티티 연합: 기업의 기존 Windows Active Directory(AD) 또는 Linux OpenLDAP과 원활히 통합해 엔터프라이즈급 통합 계정 single sign-on(SSO)을 구현한다; 워크플로우 승인 및 권한 격리(화웨이 클라우드 예시): 클라우드 인스턴스 다단계 신청·승인 워크플로우 지원(최대 5단계 승인); \u0026ldquo;삼권 분립\u0026rdquo; 보안 원칙을 관철(보안 담당자, 시스템 관리자, 감사자가 권한을 분리해 관리); 고객사 자체 개발 OA/BPM 승인 흐름 시스템 연동을 지원한다. 핵심 컴포넌트 기능 요약표: 컴포넌트 이름 코드 명칭 핵심 서비스 기능 Horizon Dashboard 통합 Web 그래픽 관리 콘솔 Nova Compute 컴퓨트 리소스 라이프사이클 및 인스턴스 스케줄링 관리 Glance Image VM 이미지 스토리지 및 버전 등록 Swift Object 대규모 비정형 객체 스토리지 서비스 Neutron Network 소프트웨어 정의 네트워크(SDN), 라우팅 및 보안 그룹 관리 Cinder Block 클라우드 인스턴스 영구 블록 스토리지(클라우드 디스크) 관리 Heat Orchestration 인프라-as-Code(IaC) 다중 리소스 스택 오케스트레이션 Ceilometer Metering 리소스 사용량 통계, 모니터링 계량 및 할당량 분석 Keystone Identity 통합 신원 인증, 인가 토큰 및 서비스 카탈로그 관리 2. OpenStack 배포 계획(Victoria 에디션) 1. 배포 방식 비교 오픈소스 수동 배포: 컴포넌트를 하나씩 컴파일·설정—번거롭고 복잡하며 보통 기반 메커니즘 심층 학습용; 도구 기반 자동화 배포: Ansible 배포(예: OpenStack-Ansible, Kolla-Ansible); Packstack 배포: Red Hat가 오픈소스로 제공하는 Puppet 기반 자동화 신속 전달 도구(본 실전에서 채택); 2. 클러스터 노드 사양 및 네트워크 계획 (1) Controller 제어 노드(1대) 탑재 컴포넌트: API 서비스: nova-api, cinder-api, glance-api, neutron-server 등; 기반 서비스: MariaDB / MySQL, RabbitMQ 메시지 큐, Memcached, Keystone; 네트워크 및 관리 서비스: 네트워크 노드 외부 라우팅 포워딩, 로컬 YUM 캐시, Chrony 시간 동기화 서버. 시스템 사양 요구: CPU: 4 vCPU 이상 메모리: 4GB 이상 디스크: 100GB 사용 가능 디스크 공간 NIC: 네트워크 카드 3개(NIC 1은 인터넷 접속용 브릿지 모드, NIC 2·3은 호스트 전용 모드로 내부 트래픽) IP 주소 계획: 내부 관리 IP: 192.168.100.10 외부 접속 IP: 192.168.10.10 (2) Compute 컴퓨트 노드(1대) 탑재 컴포넌트: nova-compute, 네트워크 L2 Agent(Open vSwitch Agent) 시스템 사양 요구: CPU: 4 vCPU 이상 메모리: 8GB 이상(최소 4GB) 디스크: 100GB 디스크 공간 NIC: 네트워크 카드 3개(전부 호스트 전용 모드) IP 주소 계획: Compute: 192.168.100.11 (3) YUM 저장소 서버 + Chrony 시간 동기화 서버(1대) 시스템 사양 요구: CPU: 2 vCPU 메모리: 2GB NIC: 네트워크 카드 2개(하나는 인터넷 브릿지로 공개 시각 동기화 및 상위 저장소용, 다른 하나는 호스트 전용으로 내부 노드 제공) IP 주소 계획: 외부 IP: 192.168.10.11 내부 IP: 192.168.100.20 환경 하향 조정 안내: 개인용 컴퓨터 하드웨어 자원이 부족하면 최소 2대의 VM으로 압축할 수 있다(제어 노드를 YUM 소스 및 시간 동기화 서버로 겸용하고, 컴퓨트 노드 1대를 추가). 주의: 모든 노드의 시스템 시각은 매우 일치해야 하며 시계 드리프트는 5분을 초과해서는 안 된다. 그렇지 않으면 Keystone 핸드셰이크와 메시지 큐 통신이 완전히 실패한다.\n3. OpenStack 자동화 배포 실전 절차 단계 1: YUM 저장소 서버 및 Chrony 시간 동기화 서버 구축 1. 네트워크 인터페이스 설정 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 # 1. 내부 통신 NIC ens160 설정 [root@Cloud ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens160 TYPE=Ethernet BOOTPROTO=none NAME=ens160 DEVICE=ens160 ONBOOT=yes IPADDR=192.168.100.20 PREFIX=24 # 2. 인터넷 연결용 NIC ens192 설정(공개 NTP 동기화용) [root@Cloud ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens192 TYPE=Ethernet BOOTPROTO=none NAME=ens192 DEVICE=ens192 ONBOOT=yes PROXY_METHOD=none BROWSER_ONLY=no IPADDR=192.168.10.11 PREFIX=24 GATEWAY=192.168.10.1 DNS1=192.168.10.1 DEFROUTE=yes IPV4_FAILURE_FATAL=no IPV6INIT=no UUID=03da7500-2101-c722-2438-d0d006c28c73 # 설정을 적용하기 위해 NIC 재시작 [root@Cloud ~]# nmcli connection down ens160 ; nmcli connection up ens160 Connection \u0026#39;ens160\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/1) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4) [root@Cloud ~]# nmcli connection down ens192 ; nmcli connection up ens192 Connection \u0026#39;ens192\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/3) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/5) 2. 로컬 CD-ROM YUM 저장소 설정 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@Cloud ~]# cd /etc/yum.repos.d/ [root@Cloud yum.repos.d]# mkdir bak [root@Cloud yum.repos.d]# mv CentOS-Linux-* bak/ # OS 설치 ISO 마운트 mount /dev/cdrom /media/ # CD-ROM 로컬 저장소 설정 파일 작성 [root@Cloud ~]# cat \u0026gt; /etc/yum.repos.d/dvd.repo \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; \u0026gt; [BaseOS] \u0026gt; name=BaseOS \u0026gt; baseurl=file:///media/BaseOS \u0026gt; gpgcheck=0 \u0026gt; \u0026gt; [AppStream] \u0026gt; name=AppStream \u0026gt; baseurl=file:///media/AppStream \u0026gt; gpgcheck=0 \u0026gt; EOF 3. Chrony 시간 동기화 서버 설정 OpenStack은 노드 간 시각 동기화 요구가 매우 엄격하므로 먼저 시간 소스를 설정한다:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # /etc/chrony.conf 편집 [root@Cloud ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool ntp.tencent.com iburst # Allow NTP client access from local network. allow 192.168.100.0/24 # Serve time even if not synchronized to a time source. local stratum 10 # 시간 동기화 서비스 재시작 및 검증 [root@Cloud ~]# systemctl restart chronyd.service [root@Cloud ~]# chronyc sources 210 Number of sources = 1 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^* 106.55.184.199 2 6 17 2 -224us[ -144us] +/- 54ms # 방화벽 및 SELinux 비활성화 [root@Cloud ~]# systemctl disable firewalld.service --now [root@Cloud ~]# setenforce 0 [root@Cloud ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config 4. httpd 기반 로컬 웹 YUM 저장소 구축 CentOS 8 및 OpenStack Victoria 소프트웨어 저장소를 HTTP 미러 소스로 게시하여 모든 클러스터 노드가 가져오도록 한다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 # 1. Apache 웹 서비스 설치 및 시작 [root@Cloud ~]# yum -y install httpd [root@Cloud ~]# systemctl enable httpd --now # 2. 디렉터리 생성 및 이미지 파일 마운트 [root@Cloud ~]# mkdir /isos [root@Cloud ~]# mkdir -p /var/www/html/centos8 [root@Cloud ~]# mkdir -p /var/www/html/openstack # 베이스 시스템 CD 및 OpenStack Victoria 설치 이미지 마운트 [root@Cloud ~]# mount /dev/cdrom /var/www/html/centos8/ [root@Cloud ~]# mount /isos/26-CentOS8-4-OSP-Victoria.iso /var/www/html/openstack/ # 3. 마운트를 /etc/fstab에 영구 기록 [root@Cloud ~]# vim /etc/fstab /dev/cdrom /var/www/html/centos8 iso9660 defaults 0 0 /isos/26-CentOS8-4-OSP-Victoria.iso /var/www/html/openstack iso9660 defaults 0 0 # 4. HTTP YUM 저장소 파일 설정 [root@Cloud ~]# vim /etc/yum.repos.d/dvd.repo [BaseOS] name=BaseOS baseurl=http://192.168.100.20/centos8/BaseOS gpgcheck=0 [AppStream] name=AppStream baseurl=http://192.168.100.20/centos8/AppStream gpgcheck=0 [root@Cloud ~]# vim /etc/yum.repos.d/openstack.repo [centos-advanced-virtualization] name=centos-advanced-virtualization baseurl=http://192.168.100.20/openstack/centos-advanced-virtualization gpgcheck=0 [centos-nfv-openvswitch] name=centos-nfv-openvswitch baseurl=http://192.168.100.20/openstack/centos-nfv-openvswitch gpgcheck=0 [centos-rabbitmq-38] name=centos-rabbitmq-38 baseurl=http://192.168.100.20/openstack/centos-rabbitmq-38 gpgcheck=0 [PowerTools] name=PowerTools baseurl=http://192.168.100.20/openstack/PowerTools gpgcheck=0 [centos-ceph-nautilus] name=centos-ceph-nautilus baseurl=http://192.168.100.20/openstack/centos-ceph-nautilus gpgcheck=0 [centos-openstack-victoria] name=centos-openstack-victoria baseurl=http://192.168.100.20/openstack/centos-openstack-victoria gpgcheck=0 [extras] name=extras baseurl=http://192.168.100.20/openstack/extras gpgcheck=0 # 5. 작성한 repo 파일을 각 제어 노드와 컴퓨트 노드에 일괄 배포 [root@Cloud ~]# scp /etc/yum.repos.d/dvd.repo /etc/yum.repos.d/openstack.repo root@192.168.100.10:/etc/yum.repos.d/ [root@Cloud ~]# scp /etc/yum.repos.d/dvd.repo /etc/yum.repos.d/openstack.repo root@192.168.100.11:/etc/yum.repos.d/ 단계 2: 클러스터 각 노드 시스템 환경 초기화 1. Controller 제어 노드 초기화(192.168.100.10) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 # 1. 관리 네트워크 ens160 설정 [root@Controller ~]# nmcli connection modify ens160 ipv4.addresses 192.168.100.10/24 ipv4.method manual autoconnect yes [root@Controller ~]# nmcli connection down ens160 ; nmcli connection up ens160 # 2. 외부 네트워크 NIC ens224 설정 [root@Controller ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens224 TYPE=Ethernet BOOTPROTO=none NAME=ens224 DEVICE=ens224 ONBOOT=yes [root@Controller ~]# nmcli connection reload [root@Controller ~]# nmcli connection modify ens224 ipv4.addresses 192.168.10.10/24 ipv4.gateway 192.168.10.1 ipv4.dns 192.168.10.1 ipv4.method manual autoconnect yes [root@Controller ~]# nmcli connection down ens224 ; nmcli connection up ens224 # 3. Chrony NTP 클라이언트 설정 [root@Controller ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool 192.168.100.20 iburst [root@Controller ~]# systemctl restart chronyd.service [root@Controller ~]# chronyc sources 210 Number of sources = 1 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^* 192.168.100.20 3 6 377 24 -22us[ -46us] +/- 43ms # 4. 호스트명 설정 [root@Controller ~]# hostnamectl set-hostname Controller # 5. 방화벽 및 SELinux 비활성화 [root@Controller ~]# systemctl disable firewalld.service --now [root@Controller ~]# setenforce 0 [root@Controller ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config # 6. 클러스터 /etc/hosts 해석 설정 [root@Controller ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 7. 기본 시스템 repo 캐시 정리 [root@Controller ~]# cd /etc/yum.repos.d [root@Controller yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ 2. Compute 컴퓨트 노드 초기화(192.168.100.11) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # 1. 관리 네트워크 설정 [root@Compute ~]# nmcli connection modify ens160 ipv4.addresses 192.168.100.11/24 ipv4.method manual autoconnect yes [root@Compute ~]# nmcli connection down ens160 ; nmcli connection up ens160 # 2. Chrony NTP 클라이언트 설정 [root@Compute ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool 192.168.100.20 iburst [root@Compute ~]# systemctl enable chronyd.service --now # 3. 호스트명 설정 [root@Compute ~]# hostnamectl set-hostname Compute # 4. 방화벽 및 SELinux 비활성화 [root@Compute ~]# systemctl disable firewalld.service --now [root@Compute ~]# setenforce 0 [root@Compute ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config # 5. 클러스터 hosts 해석 설정 [root@Compute ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 6. 기본 시스템 repo 정리 [root@Compute ~]# cd /etc/yum.repos.d [root@Compute yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ ⚠️ 핵심 점검점: 이 시점에서 모든 VM을 종료하고 문제 발생 시 신속히 롤백할 수 있도록 통합 스냅샷을 촬영할 것을 권장한다.\n단계 3: Packstack 자동화 응답 파일 배포 1. Packstack 배포 도구 설치 제어 노드(Controller)에서 실행:\n1 [root@Controller ~]# yum -y install openstack-packstack 2. 기본 응답 파일 생성 1 [root@Controller ~]# packstack --gen-answer-file=/root/answers.txt 네트워크 방식 설명: OpenStack Victoria는 OVN(Open Virtual Network)과 기존 OVS(Open vSwitch) 모두를 지원한다.\n3. 응답 파일 파라미터 정밀 커스터마이징 1 2 # 자동 감지된 외부 IP를 내부 관리 IP 192.168.100.10으로 일괄 치환 [root@Controller ~]# sed -i \u0026#39;s/192.168.10.10/192.168.100.10/g\u0026#39; /root/answers.txt /root/answers.txt를 편집하고 다음 핵심 설정 항목을 확인·수정한다:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # 1. 컴퓨트 노드 클러스터 IP 목록 지정(쉼표 구분) CONFIG_COMPUTE_HOSTS=192.168.100.11 # 2. Keystone 관리자 root 초기 비밀번호 설정 CONFIG_KEYSTONE_ADMIN_PW=000000 # 3. Demo 테넌트 네트워크 비활성화(프로덕션 규범) CONFIG_PROVISION_DEMO=n # 4. Heat 오케스트레이션 컴포넌트 지원 활성화 CONFIG_HEAT_INSTALL=y # 5. Neutron 코어 드라이버 및 네트워크 유형 설정(ML2 + OVS) CONFIG_NEUTRON_ML2_TYPE_DRIVERS=geneve,flat,vxlan,vlan CONFIG_NEUTRON_ML2_MECHANISM_DRIVERS=openvswitch CONFIG_NEUTRON_ML2_TENANT_NETWORK_TYPES=vxlan CONFIG_NEUTRON_L2_AGENT=openvswitch # 6. OVS 외부 네트워크 브리지 바인딩 설정(물리 외부 NIC ens224 연결) CONFIG_NEUTRON_OVS_BRIDGE_MAPPINGS=extnet:br-ex CONFIG_NEUTRON_OVS_BRIDGE_IFACES=br-ex:ens224 # 7. 사전 스토리지 할당량 용량 설정 CONFIG_SWIFT_STORAGE_SIZE=20G CONFIG_CINDER_VOLUMES_SIZE=50G ⚠️ 핵심 점검점: 응답 파일 수정 후, 다시 모든 VM을 종료하고 스냅샷을 촬영할 것을 권장한다.\n4. 원클릭 자동화 배포 실행 전원 켠 후 제어 노드에서 배포 명령 실행:\n1 [root@Controller ~]# packstack --answer-file=/root/answers.txt 배포 스크립트는 Puppet을 기반으로 모든 노드에서 소프트웨어 소스 설정, 데이터베이스 테이블 생성, 메시지 큐 클러스터 구축, 인증서 생성, 각 컴포넌트 협조 통합 테스트를 자동으로 완료한다. 완료되면 터미널에 Dashboard 로그인 URL과 keystonerc_admin 환경 변수 파일 경로를 출력한다.\n4. Linux 시스템 로그 관리 및 OpenStack 장애 해결 1. Linux Rsyslog 로그 레벨 및 설정 아키텍처 Linux 시스템에서 시스템 하위 계층과 대부분의 서비스 로그는 rsyslog 데몬에 의해 중앙 집중 관리된다.\n설정 파일 확인: 1 2 rpm -qc rsyslog # 핵심 메인 설정 파일: /etc/rsyslog.conf 로그 규칙 구문 분석: 1 서비스 유형(Facility).로그 레벨(Priority) 로그 저장 경로 일반적인 설정 예시:\n1 2 3 4 5 6 7 8 9 # 1. 시스템 일반 메시지: # 모든 서비스의 \u0026gt;=info 레벨 로그를 기록하되 메일, 인증 보안, cron 로그 제외 *.info;mail.none;authpriv.none;cron.none /var/log/messages # 2. 디버그 로그: *.debug /var/log/debug # 3. 오류 수준 로그: *.err /var/log/error.log 구문 규칙 핵심 사항: mail: 서비스 유형(Facility); info: 로그 우선순위 레벨; .: 이상(\u0026gt;=) 의 우선순위 로그 메시지에 일치함을 나타냄; *.info: 모든 서비스의 \u0026gt;= info 레벨 로그가 대상 파일에 집계되어 기록됨을 의미한다. 실시간 동적 로그 모니터링 명령: 1 2 3 4 5 # 시스템 로그 끝 출력 추적 tail /var/log/messages # 실시간 로그 스트림 지속 수신 tail -f /var/log/messages 2. OpenStack 커스텀 서비스 로그 트러블슈팅 가이드 트러블슈팅 원칙: 각 컴포넌트는 보통 독립적인 커스텀 로그 파일 체계(/var/log/\u0026lt;컴포넌트명\u0026gt;/ 하위)를 갖는다. 컴포넌트에 이상이 있으면 먼저 해당 컴포넌트 전용 로그를 확인하고, 커스텀 로그가 없을 때만 시스템 전역 로그 /var/log/messages로 돌아가 확인한다.\n실전 사례: Neutron 네트워크 서비스 장애 위치 확인 배포 또는 운영 중 가상 네트워크 생성이 실패하거나 OVS 포트가 올라오지 않으면, 정규식 매칭으로 오류와 경고를 직접 포착할 수 있다:\n1 2 3 4 5 # 1. 모든 Neutron 로그에서 정규식으로 오류·경고 필터링 tail -f /var/log/neutron/*.log | grep -iE \u0026#39;(err|warn)\u0026#39; # 2. neutron-server 핵심 API 서비스 로그를 심층 확인하고 매칭 항목 전후 3줄 컨텍스트 출력: tail -f /var/log/neutron/server.log | grep -iE -A3 -B3 \u0026#39;(err|warn)\u0026#39; 파라미터 설명:\n-i: 대소문자 무시; -E: 확장 정규식 매칭 (err|warn) 활성화; -A 3 (After): 매칭 행 이후 3줄 출력; -B 3 (Before): 매칭 행 이전 3줄 출력. ","date":"2026-10-06","description":"","objectID":"/ko/posts/openstack-core-architecture-deployment/","readingTime":12,"tags":["OpenStack","클라우드 컴퓨팅","자동화 배포","Packstack","Victoria"],"title":"OpenStack 클라우드 OS: 핵심 아키텍처 및 자동화 배포","url":"/ko/posts/openstack-core-architecture-deployment/","wordCount":2460},{"categories":["OpenStack"],"content":"Cinder 볼륨 생성 및 연결 아키텍처 볼륨 생성은 cinder 노드(컨트롤러 노드)가 담당하고, 볼륨 마운트는 nova가 담당합니다\n로컬 LVM + iSCSI 볼륨 스토리지 구성 answers.txt 응답 파일에서 cinder 볼륨을 생성하도록 지정합니다. 실습 환경에서는 전용 스토리지 장비를 제공할 수 없으므로 시스템은 로컬 LVM을 cinder 스토리지로 사용합니다 시스템은 기본적으로 cinder-volumes 볼륨 그룹을 50G 크기로 생성합니다(사용자가 수동 지정)\n이 볼륨 그룹은 컨트롤러 노드에 생성됩니다\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [root@Controller ~]# vgdisplay --- Volume group --- VG Name cl System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 4 VG Access read/write VG Status resizable MAX LV 0 Cur LV 3 Open LV 3 Max PV 0 Cur PV 1 Act PV 1 VG Size \u0026lt;99.00 GiB PE Size 4.00 MiB Total PE 25343 Alloc PE / Size 25343 / \u0026lt;99.00 GiB Free PE / Size 0 / 0 VG UUID K2c9TX-MlXI-k9Ga-CPPm-3zuu-WNoK-mRuUEZ --- Volume group --- VG Name cinder-volumes System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 4 VG Access read/write VG Status resizable MAX LV 0 Cur LV 1 Open LV 0 Max PV 0 Cur PV 1 Act PV 1 VG Size \u0026lt;51.50 GiB PE Size 4.00 MiB Total PE 13183 Alloc PE / Size 12551 / \u0026lt;49.03 GiB Free PE / Size 632 / \u0026lt;2.47 GiB VG UUID nXhaeU-1PwG-is18-Ef2N-cK5R-9LRk-3ksn1k /etc/cinder/cinder.conf에서 LVM 드라이버를 호출해 생성된 볼륨 그룹을 인식시킵니다 이 컨트롤러 노드를 ISCSI 스토리지(target)로 사용하여 사용자가 만든 볼륨을 ISCSI로 compute 노드에 공유합니다 사용자가 2G 클라우드 디스크를 생성하면 시스템이 자동으로 LVM 드라이버를 호출해 볼륨 그룹 안에 2G LV를 만듭니다\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 [root@Controller ~]# lvdisplay --- Logical volume --- LV Path /dev/cl/swap LV Name swap VG Name cl LV UUID f9Aqve-GgxT-gQNg-v0W0-ogyz-1cJT-WIEOMw LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 2 LV Size \u0026lt;7.88 GiB Current LE 2016 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:1 --- Logical volume --- LV Path /dev/cl/home LV Name home VG Name cl LV UUID 5f8ntZ-4pxS-yYFl-3BHC-qKaW-pFsi-2ZGkHg LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 1 LV Size 29.89 GiB Current LE 7653 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:2 --- Logical volume --- LV Path /dev/cl/root LV Name root VG Name cl LV UUID 32JpBQ-Ld4z-0bLU-jHuG-iMRC-WLoO-WtcrnT LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 1 LV Size \u0026lt;61.23 GiB Current LE 15674 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:0 --- Logical volume --- LV Name cinder-volumes-pool VG Name cinder-volumes LV UUID ughXDw-Fl7B-Mbo9-vsHS-OvMy-Pi60-E2GPaa LV Write Access read/write (activated read only) LV Creation host, time Controller, 2026-07-08 16:03:29 +0800 LV Pool metadata cinder-volumes-pool_tmeta LV Pool data cinder-volumes-pool_tdata LV Status available # open 0 LV Size \u0026lt;48.93 GiB Allocated pool data 0.00% Allocated metadata 10.44% Current LE 12525 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:6 --- Logical volume --- LV Path /dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e LV Name volume-3561d822-2929-4b47-87c5-25f769f8f44e VG Name cinder-volumes LV UUID WoSBug-zWc4-wVYn-39xk-MWVw-z3Pj-UOdvok LV Write Access read/write LV Creation host, time Controller, 2026-10-05 15:56:30 +0800 LV Pool name cinder-volumes-pool LV Status available # open 0 LV Size 2.00 GiB Mapped size 0.00% Current LE 512 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:7 사용자가 이 볼륨을 클라우드 서버 web01에 연결하면 cinder가 스토리지 측에 명령을 보냅니다. 스토리지 측은 해당 LV를 compute 호스트에 공유하고, compute 호스트가 그 디스크를 인식해 새로운 2G 디스크가 생기며 libvirt가 xml 파일을 호출해 클라우드 서버에 추가합니다\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 [root@Controller ~]# systemctl status target ● target.service - Restore LIO kernel target configuration Loaded: loaded (/usr/lib/systemd/system/target.service; enabled; vendor preset: disabled) Active: active (exited) since Mon 2026-10-05 14:18:51 CST; 1h 30min ago Process: 1873 ExecStart=/usr/bin/targetctl restore (code=exited, status=0/SUCCESS) Main PID: 1873 (code=exited, status=0/SUCCESS) Tasks: 0 (limit: 36406) Memory: 0B CGroup: /system.slice/target.service Oct 05 14:18:50 Controller systemd[1]: Starting Restore LIO kernel target configuration... Oct 05 14:18:51 Controller target[1873]: No saved config file at /etc/target/saveconfig.json, ok, exiting Oct 05 14:18:51 Controller systemd[1]: Started Restore LIO kernel target configuration. [root@Controller ~]# targetcli targetcli shell version 2.1.53 Copyright 2011-2013 by Datera, Inc and others. For help on commands, type \u0026#39;help\u0026#39;. /\u0026gt; ls o- / ......................................................................................................................... [...] o- backstores .............................................................................................................. [...] | o- block .................................................................................................. [Storage Objects: 1] | | o- iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e [/dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e (2.0GiB) write-thru activated] | | o- alua ................................................................................................... [ALUA Groups: 1] | | o- default_tg_pt_gp ....................................................................... [ALUA state: Active/optimized] | o- fileio ................................................................................................. [Storage Objects: 0] | o- pscsi .................................................................................................. [Storage Objects: 0] | o- ramdisk ................................................................................................ [Storage Objects: 0] o- iscsi ............................................................................................................ [Targets: 1] | o- iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e ............................................. [TPGs: 1] | o- tpg1 .......................................................................................... [no-gen-acls, auth per-acl] | o- acls .......................................................................................................... [ACLs: 1] | | o- iqn.1994-05.com.redhat:1220933cde3c ...................................................... [1-way auth, Mapped LUNs: 1] | | o- mapped_lun0 ................. [lun0 block/iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (rw)] | o- luns .......................................................................................................... [LUNs: 1] | | o- lun0 [block/iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (/dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e) (default_tg_pt_gp)] | o- portals .................................................................................................... [Portals: 1] | o- 192.168.100.10:3260 .............................................................................................. [OK] o- loopback ......................................................................................................... [Targets: 0] /\u0026gt; [root@Compute ~]# cat /etc/iscsi/initiatorname.iscsi InitiatorName=iqn.1994-05.com.redhat:1220933cde3c [root@Compute ~]# iscsiadm -m session tcp: [1] 192.168.100.10:3260,1 iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (non-flash) [root@Compute ~]# vim /etc/libvirt/qemu/instance-00000002.xml \u0026lt;disk type=\u0026#39;block\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;raw\u0026#39; cache=\u0026#39;none\u0026#39; io=\u0026#39;native\u0026#39;/\u0026gt; \u0026lt;source dev=\u0026#39;/dev/sda\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vdb\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;serial\u0026gt;3561d822-2929-4b47-87c5-25f769f8f44e\u0026lt;/serial\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x00\u0026#39; slot=\u0026#39;0x07\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; 스토리지 측 설정\ntargetcli 패키지를 설치하고 서비스를 시작합니다 1 2 3 4 5 6 7 8 9 #yum -y install targetcli #systemctl enable target --now #targetcli /\u0026gt; /backstores/block create dev=/dev/sdb1 name=data01 /\u0026gt; /iscsi create iqn.2024.com.storage:disk /\u0026gt; /iscsi/ iqn.2024.com.storage:disk/tpg1/acls create iqn.1994-05.com.redhat:b4ff583759f4 /\u0026gt;/iscsi/ iqn.2024.com.storage:disk /tpg1/luns create data01 /\u0026gt; /iscsi/ iqn.2024.com.storage:disk/tpg1/portals create 192.168.100.10:3260 /\u0026gt; saveconfig 타깃, 서비스 서버, 스토리지 클라이언트 1 2 3 [root@Compute ~]# iscsiadm -m discovery -t st -p 192.168.100.10 [root@Compute ~]# iscsiadm -m node -T iqn.2024.com.storage:disk -p 192.168.100.10:3260 -l #fdisk -l Cinder와 NFS 스토리지 연동 위 그림에 따라 네트워크를 계획하고 구성합니다\n1. NFS 스토리지 구성 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #配置网络 [root@storage ~]# nmcli connection modify ens160 ipv4.addresses 192.168.200.20/24 ipv4.method manual autoconnect yes [root@storage ~]# nmcli connection down ens160 ; nmcli connection up ens160 [root@storage ~]# cd /etc/sysconfig/network-scripts/ [root@storage network-scripts]# cp ifcfg-ens160 ifcfg-ens192 [root@storage network-scripts]# cat ifcfg-ens192 # Generated by parse-kickstart TYPE=Ethernet DEVICE=ens192 ONBOOT=yes BOOTPROTO=none NAME=ens192 [root@storage ~]# nmcli connection reload [root@storage ~]# nmcli connection modify ens192 ipv4.addresses 192.168.8.20/24 ipv4.method manual autoconnect yes [root@storage ~]# nmcli connection down ens192 ; nmcli connection up ens192 #安装配置安装NFS服务 [root@storage ~]# yum install -y nfs-utils rpcbind [root@storage ~]# mkdir /storage [root@storage ~]# cat /etc/exports /storage 192.168.200.0/24(rw,sync,no_root_squash) /storage 192.168.8.0/24(rw,sync,no_root_squash) [root@storage ~]# systemctl enable nfs-server --now [root@storage ~]# systemctl enable rpcbind --now 2. cinder 노드 네트워크 구성(컨트롤러 노드) 1 2 3 4 5 6 7 8 [root@Controller ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens192 TYPE=Ethernet NAME=ens192 DEVICE=ens192 ONBOOT=yes IPADDR=192.168.8.10 NETMASK=255.255.255.0 [root@Controller ~]# systemctl restart network 3. 컴퓨트 노드 네트워크 구성 1 2 [root@Compute ~]# nmcli connection modify ens192 ipv4.addresses 192.168.200.10/24 autoconnect yes ipv4.method manual [root@Compute ~]# nmcli connection down ens192 ; nmcli connection up ens192 4. cinder 구성 1 2 3 4 5 6 7 8 9 10 [root@Controller ~]# vim /etc/cinder/cinder.conf 410 default_volume_type=nfs 436 enabled_backends=lvm,nfs666 [nfs666] volume_backend_name=nfsdata volume_driver=cinder.volume.drivers.nfs.NfsDriver nfs_shares_config = /etc/cinder/nfs_shares [root@Controller ~]# cat /etc/cinder/nfs_shares storage:/storage [root@Controller ~]# systemctl restart openstack-cinder-* Cinder 노드\n1 2 [root@Controller ~]# cat /etc/hosts ####额外增加一条记录 192.168.8.20 storage 컴퓨트 노드\n1 2 3 4 5 6 [root@Compute ~]# cat /etc/hosts 192.168.200.20 storage [root@Controller ~]# source keystonerc_admin [root@Controller ~(keystone_admin)]# cinder type-create nfs [root@Controller ~(keystone_admin)]# cinder type-key nfs set volume_backend_name=nfsdata [root@Controller ~(keystone_admin)]# systemctl restart openstack-cinder-* OpenStack 웹 화면에서 볼륨을 만들고 nfs를 선택한 뒤 해당 볼륨을 클라우드 서버에 연결합니다\nOpenStack 웹 화면에서 볼륨을 만들고 nfs를 선택한 뒤 소스를 이미지로 지정합니다. 클라우드 서버 생성 시에는 방금 만든 볼륨을 소스로 선택합니다\n클라우드 서버를 만들고 나면 볼륨 하나가 생성됩니다\n기존 클라우드 서버를 삭제하고 다시 생성하면 이 볼륨을 사용할 수 있습니다\n","date":"2026-10-05","description":"","objectID":"/ko/posts/openstack-cinder-block-storage/","readingTime":7,"tags":["OpenStack","Cinder","블록 스토리지","LVM","iSCSI","NFS"],"title":"OpenStack Cinder 블록 스토리지 상세 정리: 로컬 LVM + iSCSI 볼륨과 NFS 백엔드 연동","url":"/ko/posts/openstack-cinder-block-storage/","wordCount":1468},{"categories":["OpenStack"],"content":"Keystone 서비스 OpenStack 다중 도메인 지원\n1 2 [root@Controller ~]# vim /etc/openstack-dashboard/local_settings OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True 시스템의 모든 서비스 나열\n1 [root@Controller ~(keystone_admin)]# openstack service list Keystone 주소 및 API 인터페이스\n1 OS_AUTH_URL=http://192.168.100.10:5000/v3 모든 서비스는 Keystone 주소를 알고 있습니다. 사용자 토큰이 유효한지 Keystone에 검증을 요청해야 하기 때문입니다 Keystone은 모든 서비스의 주소를 알고 있습니다. 모든 서비스는 생성 시 Keystone에 등록하기 때문입니다 엔드포인트 주소\n1 [root@Controller ~(keystone_admin)]# openstack endpoint list 각 서비스에는 엔드포인트 주소 3개가 있으며, 이 3개의 URL은 같을 수도 다를 수도 있습니다\npublic url — 외부 사용자가 접근하는 URL 주소\nadmin url — 관리자가 접근하는 URL 주소\ninternal url — 내부 컴포넌트 간 접근용 URL 주소\n클라우드 서버 제공 흐름\n사용자가 Keystone에 사용자 이름과 비밀번호로 인증을 요청하고, 인증에 성공하면 Keystone이 토큰을 발급합니다. 사용자는 그 토큰으로 nova-api에 클라우드 서버 생성 요청을 보냅니다 nova-api는 Keystone에 도움을 요청해 해당 토큰이 유효한지 검증하고, 성공하면 결과를 반환합니다 nova-api가 요청을 nova-compute 호스트로 전달합니다 nova-compute 호스트가 사용자 요청 정보에 따라 클라우드 서버를 생성하고 결과를 nova-api에 반환합니다 nova-api가 결과를 사용자에게 반환하고, 클라우드 서버 생성이 완료되며 해당 호스트 정보는 데이터베이스에 기록됩니다 nova-api에 서버 생성 요청을 보낼 때 저는 nova-api 주소를 모릅니다. 하지만 Keystone 주소는 알고 있습니다. Keystone에 nova-api 주소를 질의합니다. Keystone은 모든 서비스의 엔드포인트 주소를 저장하고 있으므로, 저는 Keystone 주소만 알면 됩니다. 다음은 Keystone 주소를 확인하고 이를 통해 다른 주소를 조회하는 방법입니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 [root@Controller ~(keystone_zlm)]# grep -E \u0026#34;^connection\\s*=.*keystone\u0026#34; /etc/keystone/*.conf /etc/keystone/keystone.conf:connection=mysql+pymysql://keystone_admin:c3fa63522e1f4586@192.168.100.10/keystone # 用户名：keystone_admin # 密码：c3fa63522e1f4586 [root@Controller ~(keystone_zlm)]# mysql -u keystone_admin -p -D keystone Enter password: Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 15494 Server version: 10.3.27-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [keystone]\u0026gt; SELECT s.type AS service_type, -\u0026gt; e.interface, -\u0026gt; e.url -\u0026gt; FROM endpoint e -\u0026gt; JOIN service s ON e.service_id = s.id -\u0026gt; ORDER BY s.type, e.interface; +----------------+-----------+--------------------------------------------------+ | service_type | interface | url | +----------------+-----------+--------------------------------------------------+ | alarming | admin | http://192.168.100.10:8042 | | alarming | internal | http://192.168.100.10:8042 | | alarming | public | http://192.168.100.10:8042 | | cloudformation | admin | http://192.168.100.10:8000/v1 | | cloudformation | internal | http://192.168.100.10:8000/v1 | | cloudformation | public | http://192.168.100.10:8000/v1 | | compute | admin | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | compute | internal | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | compute | public | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | identity | admin | http://192.168.100.10:5000 | | identity | internal | http://192.168.100.10:5000 | | identity | public | http://192.168.100.10:5000 | | image | admin | http://192.168.100.10:9292 | | image | internal | http://192.168.100.10:9292 | | image | public | http://192.168.100.10:9292 | | metering | admin | http://192.168.100.10:8777 | | metering | internal | http://192.168.100.10:8777 | | metering | public | http://192.168.100.10:8777 | | metric | admin | http://192.168.100.10:8041 | | metric | internal | http://192.168.100.10:8041 | | metric | public | http://192.168.100.10:8041 | | network | admin | http://192.168.100.10:9696 | | network | internal | http://192.168.100.10:9696 | | network | public | http://192.168.100.10:9696 | | object-store | admin | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | object-store | internal | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | object-store | public | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | orchestration | admin | http://192.168.100.10:8004/v1/%(tenant_id)s | | orchestration | internal | http://192.168.100.10:8004/v1/%(tenant_id)s | | orchestration | public | http://192.168.100.10:8004/v1/%(tenant_id)s | | placement | admin | http://192.168.100.10:8778/placement | | placement | internal | http://192.168.100.10:8778/placement | | placement | public | http://192.168.100.10:8778/placement | | volumev2 | admin | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev2 | internal | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev2 | public | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev3 | admin | http://192.168.100.10:8776/v3/%(tenant_id)s | | volumev3 | internal | http://192.168.100.10:8776/v3/%(tenant_id)s | | volumev3 | public | http://192.168.100.10:8776/v3/%(tenant_id)s | +----------------+-----------+--------------------------------------------------+ 39 rows in set (0.001 sec) MariaDB [keystone]\u0026gt; SELECT s.type AS service_type, -\u0026gt; e.interface, -\u0026gt; e.url -\u0026gt; FROM endpoint e -\u0026gt; JOIN service s ON e.service_id = s.id -\u0026gt; WHERE e.interface = \u0026#39;public\u0026#39; -\u0026gt; ORDER BY s.type; +----------------+-----------+--------------------------------------------------+ | service_type | interface | url | +----------------+-----------+--------------------------------------------------+ | alarming | public | http://192.168.100.10:8042 | | cloudformation | public | http://192.168.100.10:8000/v1 | | compute | public | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | identity | public | http://192.168.100.10:5000 | | image | public | http://192.168.100.10:9292 | | metering | public | http://192.168.100.10:8777 | | metric | public | http://192.168.100.10:8041 | | network | public | http://192.168.100.10:9696 | | object-store | public | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | orchestration | public | http://192.168.100.10:8004/v1/%(tenant_id)s | | placement | public | http://192.168.100.10:8778/placement | | volumev2 | public | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev3 | public | http://192.168.100.10:8776/v3/%(tenant_id)s | +----------------+-----------+--------------------------------------------------+ 13 rows in set (0.000 sec) MariaDB [keystone]\u0026gt; OpenStack 배포 시 서비스 생성\n데이터베이스에 들어가 데이터베이스(예: glance)를 생성합니다 데이터베이스 사용자 glance를 생성하고, glance 데이터베이스의 모든 테이블에 대한 ALL(조회·추가·수정·삭제) 권한을 부여합니다 glance 서비스를 생성하고 glance-api 주소(엔드포인트 주소)를 데이터베이스에 등록합니다 사용자가 glance-api에 접근해야 할 때 Keystone에 요청을 보내고, Keystone 데이터베이스에서 glance api 엔드포인트 주소를 조회합니다\n1 2 #示例 如果将zlm用户加入admin角色，那他就拥有了admin角色权限 [root@Controller ~(keystone_admin)]# openstack role add --user zlm --project LMandMY_HOME admin 사용자가 nova-api에 요청을 보내면 nova-api는 Keystone에 사용자 신원과 토큰 유효 기간을 검증합니다. 클라우드 서버 생성 권한이 있는지는 판단하지 않으며, 권한 여부는 각 서비스의 정책 파일에 따라 결정됩니다\n1 2 3 4 5 6 7 8 9 [root@Controller nova]# cd /etc/openstack-dashboard/ [root@Controller openstack-dashboard]# ls cinder_policy.json keystone_policy.json neutron_policy.json nova_policy.json glance_policy.json local_settings nova_policy.d [root@Controller nova(keystone_user)]# cp /etc/openstack-dashboard/glance_policy.json /etc/glance/policy.json [root@Controller nova(keystone_user)]# openstack image create --container-format bare --disk-format qcow2 --file /root/cirros-0.3.5-x86_64-disk.img --public test2 [root@Controller nova(keystone_user)]# chgrp glance /etc/glance/policy.json [root@Controller nova(keystone_user)]# systemctl restart openstack-glance-api.service Nova 서비스 컨트롤러 노드\nnova-api 모듈\nnova-scheduler 모듈\nnova-conductor 모듈\nnova-novncproxy 모듈\n컴퓨트 노드\nnova-compute 모듈\nnova-api\n외부로 제공되는 REST 인터페이스 처리 담당\n전달된 파라미터의 유효성 검증과 제약 검사를 수행합니다\n요청된 리소스에 대해 쿼터(quota) 검증 및 예약을 수행합니다\n리소스 생성·수정·삭제·조회 등\n가상 머신 라이프사이클의 진입점\n수평 확장 형태로 배포 가능\nnova-conductor\nnova-compute의 데이터베이스 직접 접근을 분리합니다. 사용자가 클라우드 서버 생성을 요청해 성공하면 nova-conductor 모듈이 해당 정보를 데이터베이스에 기록합니다\ndatabase — 해당 컴포넌트 관련 정보(클라우드 서버 정보, 이미지 정보, 네트워크 정보 등)를 저장합니다\nnova-scheduler\nOpenStack 환경에 여러 컴퓨트 노드가 있을 때 사용자가 클라우드 서버를 생성하면 nova-scheduler 모듈이 각 compute 호스트의 가중치에 따라 어느 컴퓨트 노드에서 실행할지 결정합니다\nHAProxy가 로드 밸런싱을 제공합니다. 사용자가 nova-api에 접근하려 하면 요청은 먼저 HAProxy로 전달되고, 각 nova-api의 부하 상황에 따라 어떤 API 노드로 보낼지 결정됩니다\nMQ — Message Queue(메시지 큐)\n하나의 컴포넌트가 여러 모듈로 구성될 때, 모듈 간 통신은 메시지 큐를 거쳐 혼잡 제어 등을 수행합니다\n1000명의 사용자가 nova-api에 요청을 보낸 경우\nnova-api는 해당 요청을 nova-compute로 보냅니다\nnova-api는 이 1000개의 요청을 메시지 큐로 보내고, 메시지 큐는 각 compute 호스트의 메시지 소비 상황과 처리 진도에 따라 순서대로 nova-compute 노드에 전달해 처리합니다\nnova-compute\n실제로 가상 머신을 구동하는 모듈\nnova-novncproxy\n동작 원리: nova-compute 호스트는 실행 중인 가상 머신마다 VNC Server 포트를 하나 엽니다\nnova-compute 호스트는 해당 포트를 외부에 노출해서는 안 됩니다. 노출하면 인터넷상의 호스트가 그 포트와 가상 머신에 접근할 수 있게 됩니다\n이 문제를 해결하기 위해 컨트롤러 노드에서 nova-novncproxy 서비스를 띄워 외부에 제공합니다. 사용자가 해당 가상 머신 콘솔에 접근하면 요청이 먼저 nova-novncproxy로 전달되고, 제공된 정보(가상 머신 UUID, 토큰 등)로 그 가상 머신이 어느 nova-compute에서 실행 중인지 조회한 뒤 해당 Compute 호스트로 요청을 전달합니다.\nVNC로 가상 머신을 여는 동작 원리\nvncserver — 5900 포트\nvncviewer — vncviewer\n클라이언트에 vncviewer를 설치할 필요는 없으며, 해당 기능은 브라우저 HTML에 통합되어 있습니다(noVNC 프로토콜)\nGlance 및 Swift 서비스 I. 클라우드 서버 배포:\n사용자가 클라우드 서버 배포 명령을 보내 검증을 통과하면 nova-compute 호스트가 glance 이미지 서버에서 로컬(클라우드 서버는 로컬에 배포)로 이미지를 내려받습니다 — /var/lib/nova/instances/_base/\n클라우드 서버가 부팅되면 차분 디스크가 생성되며, 새로 쓰는 데이터는 그 차분 디스크에 기록됩니다\n실습: glance 이미지를 glance 로컬 디스크가 아닌 Swift 오브젝트 스토리지에 저장하기\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@Controller ~]# cd /etc/glance/ [root@Controller glance]# cp glance-api.conf glance-api.conf.bak [root@Controller ~]# vim /etc/glance/glance-api.conf 3057 stores=file,http,swift 3111 default_store=swift 3982 swift_store_region = RegionOne 4032 swift_store_endpoint_type = publicURL 4090 swift_store_container = public 4118 swift_store_large_object_size = 5120 4142 swift_store_large_object_chunk_size = 100 4160 swift_store_create_container_on_put = True 4182 swift_store_multi_tenant = True 4230 swift_store_admin_tenants = services 4382 swift_store_auth_version = 2 4391 swift_store_auth_address = http://192.168.100.10:5000/v3 4399 swift_store_user = zlm 4408 swift_store_key = 000000 [root@Controller ~]# systemctl restart openstack-glance-api.service 참고: zlm 사용자는 swiftOperator 역할을 부여받아야 Swift에 대한 작업 권한을 가집니다\n","date":"2026-10-05","description":"","objectID":"/ko/posts/openstack-keystone-glance-nova/","readingTime":7,"tags":["OpenStack","Keystone","Glance","Nova","Endpoint","이미지 서비스"],"title":"OpenStack 핵심 서비스 상세 정리: Keystone 인증·Nova 컴퓨트·Glance/Swift 이미지 스토리지","url":"/ko/posts/openstack-keystone-glance-nova/","wordCount":1430},{"categories":["OpenStack"],"content":"I. GUI(대시보드)에서 클라우드 서버 제공 admin 사용자는 클라우드 최고 관리자입니다\nadmin은 아이덴티티를 관리합니다\n테넌트(프로젝트) 생성 사용자 생성 및 프로젝트 연결 이미지 업로드: 관리자가 업로드한 이미지는 퍼블릭(모든 테넌트 접근 가능)으로 설정할 수 있으며, 일반 사용자가 업로드한 이미지는 프라이빗으로 해당 테넌트 내에서만 공유됩니다. 플레이버 생성 외부 네트워크 생성 L3 Agent가 배포된 노드가 네트워크 노드가 됩니다\n테넌트로 로그인\n테넌트 계정으로 로그인 프라이빗 네트워크 및 서브넷(VPC) 생성 라우터 생성 및 외부/프라이빗 네트워크 연결 보안 그룹 및 규칙 생성 키 페어 생성 클라우드 서버 제공 및 플로팅 IP 연결 테스트(로그인, 호스트명, 네트워크) cloud-init이 호스트명·암호 등 사용자 정의 정보를 초기화합니다\n예를 들어 디스크 20GB를 차지하고 원활한 구동에 메모리 4GB 이상이 필요한 큰 이미지를 만들었다고 가정합니다. 하지만 테넌트는 이를 모르고 서버 제공 시 디스크 10GB·메모리 2GB 플레이버를 선택해 실패하며 원인도 모릅니다. 따라서 이미지 등록 시 최소 디스크 20GB·최소 메모리 4GB를 지정해 두면, 나중에 플레이버를 고를 때 해당 조건보다 작은 것은 선택할 수 없고 조건을 만족하는 것만 고를 수 있습니다.\n아래는 OpenStack Nova에서 새로 추가된 컴퓨트 노드를 소속 Cell 데이터베이스에 검색·매핑하기 위해 사용하는 핵심 명령입니다. Cells v2 아키텍처에서는 각 컴퓨트 노드가 반드시 이 등록 단계를 거쳐야 하며, 그렇지 않으면 스케줄러가 해당 노드에 인스턴스를 배치할 수 없고 API에도 해당 호스트가 목록에 나타나지 않습니다.\n1 [root@Controller ~(keystone_admin)]# nova-manage cell_v2 discover_hosts --verbose 클라우드 서버는 라우터를 통해 인터넷에 접근할 수 있습니다. 플로팅 IP를 구성하는 의미는 외부에 서비스를 노출하는 것입니다(웹 서버는 SNAT로 외부로 나갈 수 있지만 원격 사용자는 그 서비스에 들어올 수 없습니다). 아래는 OpenStack이 내부적으로 사용하는 iptables 규칙을 확인하는 방법입니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 [root@Controller ~(keystone_admin)]# ip netns qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 (id: 1) qdhcp-a82a6d9e-26ec-497f-8f71-68d8395386ad (id: 0) [root@Controller ~(keystone_admin)]# ip netns exec qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 ip addr list 1: lo: \u0026lt;LOOPBACK,UP,LOWER_UP\u0026gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 33: qg-d9305000-ef: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether fa:16:3e:18:37:69 brd ff:ff:ff:ff:ff:ff inet 192.168.10.106/24 brd 192.168.10.255 scope global qg-d9305000-ef valid_lft forever preferred_lft forever inet 192.168.10.182/32 brd 192.168.10.182 scope global qg-d9305000-ef valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe18:3769/64 scope link valid_lft forever preferred_lft forever 34: qr-eb02e78e-a3: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether fa:16:3e:18:b8:5f brd ff:ff:ff:ff:ff:ff inet 192.168.20.254/24 brd 192.168.20.255 scope global qr-eb02e78e-a3 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe18:b85f/64 scope link valid_lft forever preferred_lft forever [root@Controller ~(keystone_admin)]# ip netns exec qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 iptables -t nat -nL Chain PREROUTING (policy ACCEPT) target prot opt source destination neutron-l3-agent-PREROUTING all -- 0.0.0.0/0 0.0.0.0/0 Chain INPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination neutron-l3-agent-POSTROUTING all -- 0.0.0.0/0 0.0.0.0/0 neutron-postrouting-bottom all -- 0.0.0.0/0 0.0.0.0/0 Chain OUTPUT (policy ACCEPT) target prot opt source destination neutron-l3-agent-OUTPUT all -- 0.0.0.0/0 0.0.0.0/0 Chain neutron-l3-agent-OUTPUT (1 references) target prot opt source destination DNAT all -- 0.0.0.0/0 192.168.10.182 to:192.168.20.197 Chain neutron-l3-agent-POSTROUTING (1 references) target prot opt source destination ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ! ctstate DNAT Chain neutron-l3-agent-PREROUTING (1 references) target prot opt source destination REDIRECT tcp -- 0.0.0.0/0 169.254.169.254 tcp dpt:80 redir ports 9697 DNAT all -- 0.0.0.0/0 192.168.10.182 to:192.168.20.197 Chain neutron-l3-agent-float-snat (1 references) target prot opt source destination SNAT all -- 192.168.20.197 0.0.0.0/0 to:192.168.10.182 random-fully Chain neutron-l3-agent-snat (1 references) target prot opt source destination neutron-l3-agent-float-snat all -- 0.0.0.0/0 0.0.0.0/0 SNAT all -- 0.0.0.0/0 0.0.0.0/0 to:192.168.10.106 random-fully SNAT all -- 0.0.0.0/0 0.0.0.0/0 mark match ! 0x2/0xffff ctstate DNAT to:192.168.10.106 random-fully Chain neutron-postrouting-bottom (1 references) target prot opt source destination neutron-l3-agent-snat all -- 0.0.0.0/0 0.0.0.0/0 /* Perform source NAT on outgoing traffic. */ 실습 과제: 인터넷에 접근 가능한 클라우드 서버를 생성하고, yum 저장소를 Alibaba Cloud로 지정한 뒤 httpd 패키지를 설치·서비스를 시작하고, 외부 호스트에서 Web 페이지 http://192.168.10.182 에 접근한다.\nCompute 호스트는 본질적으로 KVM 하이퍼바이저입니다. OpenStack이 클라우드 서버를 제공할 때 스케줄러가 그 중 한 Compute 호스트에 배치합니다.\nII. 명령줄(CLI)에서 클라우드 서버 제공 1 [root@Controller ~(keystone_admin)]# cp keystonerc_admin keystonerc_zlm 요구사항 1: CLI로 모든 리소스 삭제\n1 2 3 4 5 6 7 8 9 10 11 12 [root@Controller ~(keystone_zlm)]# cat keystonerc_zlm unset OS_SERVICE_TOKEN export OS_USERNAME=zlm export OS_PASSWORD=\u0026#39;000000\u0026#39; export OS_REGION_NAME=RegionOne export OS_AUTH_URL=http://192.168.100.10:5000/v3 export PS1=\u0026#39;[\\u@\\h \\W(keystone_zlm)]\\$ \u0026#39; export OS_PROJECT_NAME=LMandMY_HOME export OS_USER_DOMAIN_NAME=Default export OS_PROJECT_DOMAIN_NAME=Default export OS_IDENTITY_API_VERSION=3 [root@Controller ~(keystone_zlm)]# source keystonerc_zlm 클라우드 서버 삭제 1 2 3 4 5 6 7 [root@Controller ~(keystone_zlm)]# openstack server list +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ | ID | Name | Status | Networks | Image | Flavor | +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ | 44f0fa66-94ed-44ac-a9ae-0a3a13c1faba | web | ACTIVE | VPC01=192.168.20.197, 192.168.10.182 | CentOS 8.4 64bit | m3.web | +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ [root@Controller ~(keystone_zlm)]# openstack server delete 44f0fa66-94ed-44ac-a9ae-0a3a13c1faba 볼륨 삭제 1 2 [root@Controller ~(keystone_zlm)]# openstack volume list [root@Controller ~(keystone_zlm)]# openstack volume delete data01 라우터 삭제 1 2 3 4 5 [root@Controller ~(keystone_zlm)]# openstack router list [root@Controller ~(keystone_zlm)]# openstack router show router01 [root@Controller ~(keystone_zlm)]# openstack subnet list [root@Controller ~(keystone_zlm)]# openstack router remove subnet router01 subnet01 [root@Controller ~(keystone_zlm)]# openstack router delete router01 보안 그룹 삭제 1 2 [root@Controller ~(keystone_zlm)]# openstack security group list [root@Controller ~(keystone_zlm)]# openstack security group delete SG01 네트워크 삭제 1 2 [root@Controller ~(keystone_zlm)]# openstack network list [root@Controller ~(keystone_zlm)]# openstack network delete VPC01 admin 계정으로 전환하여 퍼블릭 네트워크·이미지·플레이버·테넌트·사용자 등을 삭제합니다.\n1 [root@Controller ~(keystone_zlm)]# source keystonerc_admin 퍼블릭 네트워크 삭제 1 2 [root@Controller ~(keystone_admin)]# openstack network list [root@Controller ~(keystone_admin)]# openstack network delete public 이미지 삭제 1 2 3 4 [root@Controller ~(keystone_admin)]# openstack image list [root@Controller ~(keystone_admin)]# openstack image delete e3336d90-e673-4b1b-82ec-69b70fd6402c [root@Controller ~(keystone_admin)]# openstack image delete cec813dd-8141-4a99-8883-805ac6e3ec17 [root@Controller ~(keystone_admin)]# openstack image delete c6ffe7db-ccb0-4df4-8715-e8ce0fd5dc7d 플레이버 삭제 1 2 [root@Controller ~(keystone_admin)]# openstack flavor list [root@Controller ~(keystone_admin)]# openstack flavor delete m3.web 테넌트 및 사용자 삭제 1 2 3 4 [root@Controller ~(keystone_admin)]# openstack project list [root@Controller ~(keystone_admin)]# openstack project delete LMandMY_HOME [root@Controller ~(keystone_admin)]# openstack user list [root@Controller ~(keystone_admin)]# openstack user delete zlm 요구사항 2: CLI로 모든 리소스 생성—서버 제공, 플로팅 IP 추가, 서버 접속, 네트워크 생성\n1 [root@Controller ~(keystone_admin)]# source keystonerc_admin 프로젝트 사용자를 생성하고 연결 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@Controller ~(keystone_admin)]# openstack project create LMandMY_HOME [root@Controller ~(keystone_admin)]# openstack user create --project LMandMY_HOME --project-domain Default --password-prompt zlm [root@Controller ~(keystone_admin)]# openstack role list +----------------------------------+------------------+ | ID | Name | +----------------------------------+------------------+ | 0adc17230a5a434195d5995863280b3d | heat_stack_owner | | 2e5abdde67bb4f23bbf15454616af53e | ResellerAdmin | | 46d49fecd6f4411bbd72d0b9f575bcae | admin | | 5f5bf78c40b143bbb2df74fc855ea204 | reader | | 84ebb81407f644b18e1a529e0c317f16 | member | | b83f4c27c83445489554e988fb9af5a1 | heat_stack_user | | b8f8cd45891240bfa0f80e93f0e37ab9 | _member_ | | ebc22a15458c4712bf0dd4f4de09e0bf | SwiftOperator | +----------------------------------+------------------+ [root@Controller ~(keystone_admin)]# openstack role add --user zlm --project LMandMY_HOME b8f8cd45891240bfa0f80e93f0e37ab9 이미지 업로드 1 [root@Controller ~(keystone_admin)]# openstack image create --file /data/cirros-0.3.5-x86_64-disk.img --disk-format qcow2 --container-format bare --public cirros 플레이버 생성 1 [root@Controller ~(keystone_admin)]# openstack flavor create --ram 1024 --disk 10 --public m3.web 퍼블릭 네트워크 및 서브넷 생성 1 [root@Controller ~(keystone_admin)]# openstack network create --external --share --provider-network-type flat --provider-physical-network extnet --project LMandMY_HOME public 서브넷 생성\n1 [root@Controller ~(keystone_admin)]# openstack subnet create --no-dhcp --subnet-range 192.168.10.0/24 --allocation-pool start=192.168.10.100,end=192.168.10.200 --gateway 192.168.10.1 --network public publicsub01 테넌트 계정으로 전환\n1 [root@Controller ~(keystone_admin)]# source keystonerc_zlm 네트워크 및 서브넷 생성 1 [root@Controller ~(keystone_zlm)]# openstack network create VPC01 서브넷 생성\n1 [root@Controller ~(keystone_zlm)]# openstack subnet create --network VPC01 --subnet-range 192.168.20.0/24 --dhcp --gateway 192.168.20.254 --dns-nameserver 114.114.114.114 subnet01 라우터 생성 1 2 3 [root@Controller ~(keystone_zlm)]# openstack router create router01 [root@Controller ~(keystone_zlm)]# openstack router add subnet router01 subnet01 [root@Controller ~(keystone_zlm)]# openstack router set --external-gateway public router01 보안 그룹 생성 1 [root@Controller ~(keystone_zlm)]# openstack security group create SG01 ICMP 프로토콜 허용\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol icmp --remote-ip 0.0.0.0/0 SG01 SSH 서비스 허용\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol tcp --dst-port 22 --remote-ip 0.0.0.0/0 SG01 포트 80 허용\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol tcp --dst-port 80 --remote-ip 0.0.0.0/0 SG01 플로팅 IP 생성 1 [root@Controller ~(keystone_zlm)]# openstack floating ip create public 키 페어 생성 1 [root@Controller ~(keystone_zlm)]# openstack key create key01 \u0026gt; key01.pem 클라우드 서버 생성 1 2 3 4 5 6 7 8 [root@Controller ~(keystone_zlm)]# openstack network list +--------------------------------------+--------+--------------------------------------+ | ID | Name | Subnets | +--------------------------------------+--------+--------------------------------------+ | a82a6d9e-26ec-497f-8f71-68d8395386ad | VPC01 | 40bd75e0-3d22-4e7b-8659-8a6c0a44e3aa | | d99ba601-05e3-4f1f-88b8-f0587c6a5f6e | public | 5d39db23-8359-4e33-bde7-0f9ac6a0f7c7 | +--------------------------------------+--------+--------------------------------------+ [root@Controller ~(keystone_zlm)]# openstack server create --image cirros --security-group SG01 --key-name key01 --flavor m3.web --nic net-id=a82a6d9e-26ec-497f-8f71-68d8395386ad web 플로팅 IP를 클라우드 서버에 연결\n1 2 [root@Controller ~(keystone_zlm)]# openstack port list [root@Controller ~(keystone_zlm)]# openstack floating ip set --port=46ba21d5-6f14-4693-a215-5a6fc8781adf 192.168.10.182 클라우드 서버 접속\n1 2 [root@Controller ~(keystone_zlm)]# chmod 400 key01.pem [root@Controller ~(keystone_zlm)]# ssh -i key01.pem cirros@192.168.10.182 외부 NIC 통신이 안 되는 경우\n1 [root@Controller ~(keystone_zlm)]# ifup br-ex ; ifup ens224 ","date":"2026-10-04","description":"","objectID":"/ko/posts/openstack-provision-cloud-server/","readingTime":7,"tags":["OpenStack","클라우드 서버","Horizon","Neutron","Nova","CLI"],"title":"OpenStack 클라우드 서버 프로비저닝","url":"/ko/posts/openstack-provision-cloud-server/","wordCount":1423},{"categories":["MySQL"],"content":"01 - systemctl 데이터베이스 Service 파일 정리 방안 1: 표준 운영 데몬 구성 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [Unit] Description=MySQL Server After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid ExecReload=/bin/kill -HUP $MAINPID PIDFile=/var/run/mysqld/mysqld.pid PrivateTmp=true RuntimeDirectory=mysqld [Install] WantedBy=multi-user.target 방안 2: 설정 파일 및 파일 핸들 제한 포함 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Unit] Description=MySQL Server After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf RuntimeDirectory=mysqld LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target 수정 완료 후\n1 2 3 [root@master local]#systemctl daemon-reload [root@master local]#systemctl restart mysqld.service [root@master local]#systemctl enable mysqld.service 02 - 멀티버전 MySQL (5.6 / 5.7) 설치 및 업그레이드 준비 실전 1단계: 소프트웨어 아카이브 다운로드 1 2 3 4 5 # MySQL 5.7.40 바이너리 패키지 [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.40-linux-glibc2.12-x86_64.tar.gz # MySQL 5.6.48 바이너리 패키지 [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.48-linux-glibc2.12-x86_64.tar.gz 2단계: 압축 해제, 설치 및 심볼릭 링크 생성 1 2 3 4 5 6 7 8 9 # 5.7 버전 설치 [root@master local]#tar xf mysql-5.7.40-linux-glibc2.12-x86_64.tar.gz [root@master local]#ln -s mysql-5.7.40-linux-glibc2.12-x86_64 mysql57 # 참고: 멀티버전 공존 디버깅 기간에는 전역 환경변수를 아직 설정하지 말고 명령 경로 충돌 방지 # 5.6 버전 설치 [root@master local]#tar xf mysql-5.6.48-linux-glibc2.12-x86_64.tar.gz [root@master local]#ln -s mysql-5.6.48-linux-glibc2.12-x86_64 mysql56 # 참고: 당분간 전역 환경변수 설정하지 않음 3단계: 데이터베이스 소프트웨어 초기화 MySQL 5.7 초기화 (데이터 디렉터리 3357):\n1 2 3 4 5 [root@master local]#mkdir -p /data/3357/data [root@master local]#chown -R mysql.mysql /data/3357/data [root@master local]#/usr/local/mysql57/bin/mysqld --initialize-insecure --user=mysql --datadir=/data/3357/data --basedir=/usr/local/mysql57 [root@master local]#echo $? 0 MySQL 5.6 초기화 (데이터 디렉터리 3356):\n1 2 3 4 5 6 7 8 9 10 11 [root@master local]#mkdir -p /data/3356/data [root@master local]#chown -R mysql.mysql /data/3356/data [root@master local]#/usr/local/mysql56/scripts/mysql_install_db --user=mysql --datadir=/data/3356/data --basedir=/usr/local/mysql56 FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql56/scripts/mysql_install_db: Data::Dumper # Perl 모듈 누락 [root@master local]#yum install -y perl-Data-Dumper [root@master local]#/usr/local/mysql56/scripts/mysql_install_db --user=mysql --datadir=/data/3356/data --basedir=/usr/local/mysql56 # 출력에 OK 두 개가 포함되는지 확인하고 종료 코드를 확인: [root@master local]#echo $? 0 4단계: 다중 인스턴스 전용 설정 파일 작성 MySQL 5.7 (인스턴스 3357) 설정:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@master local]#vim /etc/my3357.cnf [mysql] socket=/tmp/mysql3357.sock [mysqld] user=mysql basedir=/usr/local/mysql57 datadir=/data/3357/data socket=/tmp/mysql3357.sock port=3357 pid-file=/run/mysqld3357/mysqld.pid [root@master local]#vim /usr/lib/systemd/system/mysqld3357.service [Unit] Description=MySQL 5.7 Server (3357) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql57/bin/mysqld --defaults-file=/etc/my3357.cnf RuntimeDirectory=mysqld3357 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target MySQL 5.6 (인스턴스 3356) 설정:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@master local]#vim /etc/my3356.cnf [mysql] socket=/tmp/mysql3356.sock [mysqld] user=mysql basedir=/usr/local/mysql56 datadir=/data/3356/data socket=/tmp/mysql3356.sock port=3356 pid-file=/run/mysqld3356/mysqld.pid [root@master local]#vim /usr/lib/systemd/system/mysqld3356.service [Unit] Description=MySQL 5.6 Server (3356) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql56/bin/mysqld --defaults-file=/etc/my3356.cnf RuntimeDirectory=mysqld3356 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target 5단계: 실행, 시작 및 관리 검증 MySQL 5.7 서비스 시작/중지:\n1 2 3 4 5 6 7 8 9 10 11 # 방식 1: 시작 [root@master local]#cp /usr/local/mysql57/support-files/mysql.server /etc/init.d/mysqld57 [root@master local]#chmod +x /etc/init.d/mysqld57 [root@master local]#/etc/init.d/mysqld57 start [root@master local]#/etc/init.d/mysqld57 stop # 방식 2: 시작 [root@master local]#systemctl daemon-reload [root@master local]#systemctl start mysqld3357.service [root@master local]#systemctl enable mysqld3357.service Created symlink from /etc/systemd/system/multi-user.target.wants/mysqld3357.service to /usr/lib/systemd/system/mysqld3357.service. MySQL 5.6 서비스 시작/중지:\n1 2 3 4 5 6 7 8 9 10 11 # 방식 1: 시작 [root@master local]#cp /usr/local/mysql56/support-files/mysql.server /etc/init.d/mysqld56 [root@master local]#chmod +x /etc/init.d/mysqld56 [root@master local]#/etc/init.d/mysqld56 start [root@master local]#/etc/init.d/mysqld56 stop # 방식 2: 시작 [root@master local]#systemctl daemon-reload [root@master local]#systemctl start mysqld3356.service [root@master local]#systemctl enable mysqld3356.service Created symlink from /etc/systemd/system/multi-user.target.wants/mysqld3356.service to /usr/lib/systemd/system/mysqld3356.service. 세 서비스 상태 검증 (모두 running)\n1 2 3 4 5 6 7 8 [root@master local]#for svc in mysqld mysqld3356 mysqld3357; do \u0026gt; a=$(systemctl show -p ActiveState \u0026#34;$svc\u0026#34; | cut -d= -f2) \u0026gt; s=$(systemctl show -p SubState \u0026#34;$svc\u0026#34; | cut -d= -f2) \u0026gt; echo \u0026#34;$svc: $a ($s)\u0026#34; \u0026gt; done mysqld: active (running) mysqld3356: active (running) mysqld3357: active (running) ","date":"2026-10-03","description":"","objectID":"/ko/posts/systemd-mysql-multiversion/","readingTime":3,"tags":["MySQL","systemd","멀티버전 배포"],"title":"systemd 서비스 관리와 MySQL 멀티버전 배포","url":"/ko/posts/systemd-mysql-multiversion/","wordCount":612},{"categories":["MySQL"],"content":"01 - 데이터베이스 서비스 설치 및 배포 1.1 데이터베이스 서버 패키지 다운로드 공식 다운로드 주소 (역사 보관 바이너리 설치 패키지):\n1 2 3 # 저장 디렉터리 [root@master ~]#cd /usr/local/ [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz 설치 패키지 정보 확인\n1 2 [root@master local]#ll mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz -rw-r--r-- 1 root root 914806904 Jul 2 2021 mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz 1.2 바이너리 패키지로 데이터베이스 서버 설치 1단계: 운영체제 환경 준비 1. 네트워크 주소 확인 1 2 3 4 5 6 7 [root@master local]#ip addr show eth0 2: eth0: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:0c:29:4b:b7:38 brd ff:ff:ff:ff:ff:ff inet 11.0.1.101/24 brd 11.0.1.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::20c:29ff:fe4b:b738/64 scope link valid_lft forever preferred_lft forever 2. 호스트명 해석 설정 1 2 3 4 [root@master local]#vim /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 11.0.1.101 master 3. 시스템 방화벽 비활성화 1 [root@master local]#systemctl disable firewalld --now 4. SELinux 비활성화 1 2 3 4 5 [root@master local]#sed -i \u0026#39;s/^SELINUX=enforcing/SELINUX=disabled/\u0026#39; /etc/selinux/config # 재부팅 적용 [root@master local]#reboot [root@master local]#getenforce Disabled 5. 데이터베이스 설치 시 충돌 방지 1 2 3 4 5 6 # 시스템에 기본 포함된 mariadb가 있는지 확인 [root@master local]#rpm -qa | grep mariadb mariadb-libs-5.5.68-1.el7.x86_64 # 시스템 기본 데이터베이스 패키지 제거 [root@master local]#yum remove -y mariadb-libs 6. 데이터베이스 의존 소프트웨어 설치 (누락 시 초기화 오류) 1 [root@master local]#yum install -y libaio-devel 2단계: 데이터베이스 소프트웨어 설치 1. 패키지 압축 해제 및 심볼릭 링크 생성 (버전 관리 용이) 1 2 [root@master local]#tar xf mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz [root@master local]#ln -s mysql-8.0.26-linux-glibc2.12-x86_64 mysql 2. 시스템 전역 환경변수 설정 1 2 3 4 5 [root@master local]#vim /etc/profile # 파일 끝에 추가 export PATH=$PATH:/usr/local/mysql/bin # 설정 적용 [root@master local]#source /etc/profile 3. 설치 버전 확인 1 2 [root@master local]#mysql -V mysql Ver 8.0.26 for Linux on x86_64 (MySQL Community Server - GPL) 3단계: 소프트웨어 초기화 MySQL 의 핵심 디렉터리 두 개:\n프로그램 디렉터리: /usr/local/mysql 데이터 디렉터리: /data/3306/data (실제 데이터 저장) 1. 전용 실행 사용자 생성 (시스템 로그인 불필요) 1 [root@master local]#useradd mysql -M -s /sbin/nologin 2. 데이터 저장 디렉터리 생성 및 권한 부여 1 2 [root@master local]#mkdir /data/3306/data -p [root@master local]#chown mysql.mysql /data/3306/data 3. 데이터 디렉터리 초기화 실행 1 2 3 4 5 # 존재할 수 있는 간섭 설정 파일 제거 [root@master local]#rm -f /etc/my.cnf # 비밀번호 없이 초기화 실행 (초기화 후 관리자 비밀번호 없음) [root@master local]#mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 4단계: 데이터베이스 서비스 설정 파일 작성 /etc/my.cnf 생성 및 편집:\n1 2 3 4 5 6 7 8 9 10 [root@master local]#cat \u0026gt; /etc/my.cnf \u0026lt;\u0026lt; \u0026#39;eof\u0026#39; \u0026gt; [mysql] \u0026gt; socket=/tmp/mysql.sock \u0026gt; \u0026gt; [mysqld] \u0026gt; user=mysql \u0026gt; basedir=/usr/local/mysql \u0026gt; datadir=/data/3306/data \u0026gt; socket=/tmp/mysql.sock \u0026gt; eof 참고: 설정 파일의 basedir 및 datadir 경로는 실제 환경에 맞게 조정해야 함.\n5단계: 데이터베이스 서비스 시작 1. 공식 서비스 관리 스크립트로 시작 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@master local]#cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld [root@master local]#/etc/init.d/mysqld start Starting MySQL.Logging to \u0026#39;/data/3306/data/master.err\u0026#39;. . SUCCESS! # 정상적으로 데이터베이스에 접속하면 시작 성공 [root@master local]#mysql -uroot Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; 2. 명령줄에서 직접 시작 mysqld 명령으로 시작: 1 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/data/3306/data --user=mysql --socket=/tmp/mysql.sock \u0026amp; mysqld_safe 안전 데몬으로 시작: 1 /usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data \u0026amp; 3. systemctl / service 로 시작 방법 1: 전용 Systemd Unit 파일 작성 (/etc/systemd/system/mysqld.service) 방법 2: /etc/init.d/ 디렉터리의 서비스 스크립트를 직접 활성화 및 호출: 1 2 3 4 systemctl enable mysqld service mysqld start # 또는 systemctl start mysqld 1.3 보충 지식 1. 초기화 방법 두 가지 비교 방법 1: 비보안 초기화 (--initialize-insecure)\n1 mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 특징: DB 관리자에게 비밀번호가 없어 바로 비밀번호 없이 로그인 가능하며, 테스트나 스크립트 초기화에 편리. 방법 2: 보안 초기화 (--initialize)\n1 mysqld --initialize --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 특징: DB 관리자에게 무작위 임시 비밀번호가 생성되어 로그에 출력되며, 최초 로그인에 사용. 주의: 임시 비밀번호는 최초 로그인 전용이며, 로그인 후 반드시 새 비밀번호로 재설정해야 하며 변경 전에는 다른 작업을 실행할 수 없음: 1 ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; 2. 데이터베이스 시작 실패 표준 해결 절차 1단계: 시스템 기본 환경 확인 SELinux 가 완전히 비활성화되었는지 확인; 포트 3306 이 점유되지 않았는지, 다른 충돌하는 데이터베이스 서비스 프로세스가 있는지 확인. 2단계: 오류 로그 조사 및 분석 전형적인 오류: Starting MySQL. ERROR! ... PID file ... 오류 로그 상세 확인: 1 2 3 cat /data/3306/data/master.err # 로그 오류 예시: # 2024-06-14T09:15:36.120048Z 1 [ERROR] [MY-012271] [InnoDB] The innodb_system data file \u0026#39;ibdata1\u0026#39; must be writable 3단계: 오류 로그가 없을 때 조사 방법 설정 파일 /etc/my.cnf 확인: 설정 파일 경로와 파일 이름이 올바른지 확인; 설정 파일 내부 파라미터 구문 및 경로 설정이 올바른지 확인. 02 - 데이터베이스 서비스 시작 및 중지 데이터베이스 서비스를 시작하고 중지하는 방법은 여러 가지이며, 일반적인 방법을 아래에 정리함:\n번호 시작 방법 해당 중지 방법 설명 및 권장 01 mysqld \u0026amp; kill / pkill / killall 비권장; kill -9 사용 엄금 02 mysqld_safe \u0026amp; kill / pkill / killall 직접 kill 비권장 03 mysql.server start mysql.server stop 공식 기본 제어 스크립트 04 service mysqld start service mysqld stop 권장 (고전 서비스 관리) 05 systemctl start mysqld systemctl stop mysqld 권장 (현대 Linux 표준) 06 클라이언트/관리 명령 mysqladmin -uroot -p비밀번호 shutdown 관리자가 원격 또는 로컬 명령줄에서 정상 종료 07 SQL 세션 내 종료 터미널에서 shutdown; 실행 관리자 권한 필요 03 - 데이터베이스 서비스 비밀번호 관리 1. 비밀번호가 없을 때 설정 방법 1: 명령줄 이용\n1 2 3 [root@master local]#mysqladmin -uroot password \u0026#39;123456\u0026#39; mysqladmin: [Warning] Using a password on the command line interface can be insecure. Warning: Since password will be sent to server in plain text, use ssl connection to ensure password safety. 방법 2: SQL 문 이용\n1 2 mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;000000\u0026#39;; Query OK, 0 rows affected (0.02 sec) 2. 기존 비밀번호 변경 방법 1: 명령줄 이용 1 2 3 [root@master local]#mysqladmin -uroot -p000000 password \u0026#39;123456\u0026#39; mysqladmin: [Warning] Using a password on the command line interface can be insecure. Warning: Since password will be sent to server in plain text, use ssl connection to ensure password safety. 방법 2: SQL 문 이용 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@master local]#mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 14 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; 3. 비밀번호를 잊어버린 경우 (비밀번호 재설정) 비밀번호 재설정 실전 절차 1단계: 데이터베이스 서비스 중지\n1 mysql\u0026gt; shutdown; 또는 시스템 터미널에서 실행:\n1 2 3 4 [root@master local]#service mysqld stop Shutting down MySQL.. SUCCESS! # 또는 pkill mysqld 2단계: 안전 모드로 시작 (권한 테이블 로드 건너뛰기로 비밀번호 없이 로그인)\n1 2 [root@master local]#/usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data --skip-grant-tables --skip-networking \u0026amp; [1] 2658 설명: --skip-networking 은 네트워크 수신을 비활성화하여 비밀번호 없는 동안 다른 네트워크 클라이언트의 개입을 방지함.\n3단계: 비밀번호 없이 로그인 후 재설정\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@master local]#mysql Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 7 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; FLUSH PRIVILEGES; Query OK, 0 rows affected (0.01 sec) mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; MySQL 터미널에서 순서대로 실행:\n1 2 3 4 5 6 7 -- 1. 권한 테이블 리플러시 (메모리와 디스크의 권한 테이블 동기화) -- 작용1: 메모리의 권한 테이블 정보를 디스크에 동기화 기록 -- 작용2: 디스크의 권한 테이블 정보를 메모리로 재로드 FLUSH PRIVILEGES; -- 2. 비밀번호 재설정 ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; 주의: FLUSH PRIVILEGES; 를 실행하지 않고 ALTER USER 를 바로 실행하면 시스템이 오류를 발생:\n1 ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement 4단계: 데이터베이스 서비스 정상 재시작\n1 2 3 4 5 6 7 # 임시 안전 모드 프로세스 종료 [root@master local]#pkill mysqld # 데이터베이스 서비스 정상 시작 [root@master local]#/etc/init.d/mysqld start Starting MySQL.. SUCCESS! # 또는 service mysqld start 5단계: 새 비밀번호로 로그인 검증\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@master local]#mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; ","date":"2026-10-03","description":"","objectID":"/ko/posts/mysql-binary-deploy/","readingTime":8,"tags":["MySQL","데이터베이스","바이너리 설치"],"title":"MySQL 실전: 바이너리 설치, 시작/중지, 비밀번호 관리","url":"/ko/posts/mysql-binary-deploy/","wordCount":1566},{"categories":["DevOps"],"content":"01. SonarQube 소개 1 2 3 1. SonarQube 은 Java 로 개발되었으며 Open JDK 8 이 필요 2. SonarQube 은 MySQL 데이터베이스에 의존하며 최소 5.6 이상 3. SonarQube 소규모 인스턴스는 최소 4G, 대규모 인스턴스는 16G 메모리 필요 02. 배포 1. JDK 실행 환경 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@sonarqube ~]#ll total 166040 -rw-r--r-- 1 root root 170023183 Oct 1 22:32 jdk-8u181-linux-x64.rpm [root@sonarqube ~]#rpm -ivh jdk-8u181-linux-x64.rpm warning: jdk-8u181-linux-x64.rpm: Header V3 RSA/SHA256 Signature, key ID ec551f03: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jdk1.8-2000:1.8.0_181-fcs ################################# [100%] Unpacking JAR files... tools.jar... plugin.jar... javaws.jar... deploy.jar... rt.jar... jsse.jar... charsets.jar... localedata.jar... 2. 데이터베이스 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 # 1. MySQL 공식 Yum 저장소 설정 파일 다운로드 [root@sonarqube ~]#wget dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm # 2. MySQL 저장소 설치 [root@sonarqube ~]#rpm -ivh mysql-community-release-el6-5.noarch.rpm Preparing... ################################# [100%] Updating / installing... 1:mysql-community-release-el6-5 ################################# [100%] # 3. MySQL 서버 설치 [root@sonarqube ~]# yum install mysql-community-server -y [root@sonarqube ~]#mysql --version mysql Ver 14.14 Distrib 5.6.51, for Linux (x86_64) using EditLine wrapper # 4. MySQL 서비스 시작 [root@sonarqube ~]#service mysqld start Starting mysqld (via systemctl): [ OK ] [root@sonarqube ~]#netstat -ntpl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1227/master tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1008/sshd tcp6 0 0 ::1:25 :::* LISTEN 1227/master tcp6 0 0 :::3306 :::* LISTEN 3113/mysqld tcp6 0 0 :::22 :::* LISTEN 1008/sshd # 5. 비밀번호 설정 [root@sonarqube ~]# mysqladmin -uroot password 000000 # 6. sonarqube 데이터베이스 생성 [root@sonarqube ~]# mysql -uroot -p000000 -e \u0026#34;CREATE DATABASE sonarqube DEFAULT CHARACTER SET utf8;\u0026#34; [root@sonarqube ~]#mysql -u root -p000000 -e \u0026#34;show databases\u0026#34; Warning: Using a password on the command line interface can be insecure. +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sonarqube | +--------------------+ 3. SonarQube 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 # 1. SonarQube 패키지 업로드 [root@sonarqube ~]# cd /usr/local/ [root@sonarqube local]#ll sonarqube-7.0.zip -rw-r--r-- 1 root root 155709573 Oct 1 22:50 sonarqube-7.0.zip # 2. 현재 디렉터리에 압축 해제 [root@sonarqube local]#yum install -y unzip [root@sonarqube local]# unzip sonarqube-7.0.zip [root@sonarqube local]# ln -s sonarqube-7.0 sonarqube # 3. 플러그인 설치: 기본 플러그인 삭제: [root@sonarqube ~]# rm -rf /usr/local/sonarqube/extensions/plugins/* 플러그인 업로드: [root@sonarqube ~]#ll sonar_plugins.tar.gz -rw-r--r-- 1 root root 45106788 Oct 1 22:56 sonar_plugins.tar.gz [root@sonarqube ~]#tar xf sonar_plugins.tar.gz -C /usr/local/sonarqube/extensions/plugins/ [root@sonarqube ~]#ll /usr/local/sonarqube/extensions/plugins/ total 4 drwxr-xr-x 2 1000 1000 4096 Oct 24 2019 plugins [root@sonarqube ~]# cd /usr/local/sonarqube/extensions/plugins/ [root@sonarqube plugins]# mv plugins/* . [root@sonarqube plugins]#rm -fr plugins/ # 4. 일반 사용자 생성 [root@sonarqube ~]# useradd sonarqube # 5. 코드 소유자를 sonarqube 로 변경 [root@sonarqube ~]# chown -R sonarqube.sonarqube /usr/local/sonarqube/ [root@sonarqube ~]# ll /usr/local/sonarqube/ total 12 drwxr-xr-x 8 sonarqube sonarqube 136 Feb 2 2018 bin drwxr-xr-x 2 sonarqube sonarqube 50 May 15 15:09 conf -rw-r--r-- 1 sonarqube sonarqube 7651 Feb 2 2018 COPYING drwxr-xr-x 2 sonarqube sonarqube 24 Feb 2 2018 data drwxr-xr-x 7 sonarqube sonarqube 150 Feb 2 2018 elasticsearch drwxr-xr-x 4 sonarqube sonarqube 40 Feb 2 2018 extensions drwxr-xr-x 9 sonarqube sonarqube 138 Feb 2 2018 lib drwxr-xr-x 2 sonarqube sonarqube 6 Feb 2 2018 logs drwxr-xr-x 2 sonarqube sonarqube 24 Feb 2 2018 temp drwxr-xr-x 9 sonarqube sonarqube 4096 Feb 2 2018 web # 6. 데이터베이스 연결 설정 [root@sonarqube ~]#vim /usr/local/sonarqube/conf/sonar.properties ... sonar.jdbc.username=root # 데이터베이스 사용자 이름 sonar.jdbc.password=000000 # 데이터베이스 비밀번호 #----- Embedded Database (default) # H2 embedded database server listening port, defaults to 9092 #sonarqube.embeddedDatabase.port=9092 #----- MySQL 5.6 or greater # Only InnoDB storage engine is supported (not myISAM). # Only the bundled driver is supported. It can not be changed. sonar.jdbc.url=jdbc:mysql://localhost:3306/sonarqube?useUnicode=true\u0026amp;characterE ncoding=utf8\u0026amp;rewriteBatchedStatements=true\u0026amp;useConfigs=maxPerformance\u0026amp;useSSL =false # 로컬 연결 활성화 ... 4. SonarQube 실행 1 2 SonarQube 은 root 로 시작할 수 없으며 반드시 일반 사용자로 실행해야 함 [root@sonarqube ~]# su - sonarqube -c \u0026#34;/usr/local/sonarqube/bin/linux-x86-64/sonar.sh start\u0026#34; 5. 원격 접속 토큰 설정 시작 후 포트는 9000 이며, 브라우저에서 http://11.0.1.202:9000/about 에 접속해 admin / admin 으로 로그인하고 토큰을 생성한다（한 번만 표시되므로 기록해 둘 것).\n1 2 3 4 5 [root@jenkins ~]# sonar-scanner \\ -Dsonar.projectKey=html \\ -Dsonar.sources=. \\ -Dsonar.host.url=http://11.0.1.202:9000 \\ -Dsonar.login=a3bd8e829343edabbbc58fefe0d3e97fdb12268b 6. 클라이언트 배포 클라이언트 IP: 11.0.1.201\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # 1. 클라이언트 업로드 [root@jenkins ~]# cd /usr/local/ [root@jenkins ~]#ll sonar-scanner-cli-4.2.0.1873-linux.zip -rw-r--r-- 1 root root 42397119 Oct 2 00:09 sonar-scanner-cli-4.2.0.1873-linux.zip # 2. 압축 해제 및 심볼릭 링크 생성 [root@jenkins local]#yum install -y unzip [root@jenkins local]#unzip /root/sonar-scanner-cli-4.2.0.1873-linux.zip [root@jenkins local]# ln -s sonar-scanner-4.2.0.1873-linux sonar # 3. 전역 PATH 변수 설정 [root@jenkins ~]# export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 4. 변수를 /etc/profile 에 영구 저장 [root@jenkins ~]# vim /etc/profile .. export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 5. 클라이언트에서 서버로 코드를 스캔해 분석 테스트 /var/lib/jenkins/workspace/test_job/ 디렉터리의 blog 코드를 SonarQube 에 푸시하여 테스트 [root@jenkins ~]#cd /var/lib/jenkins/workspace/test_job/ [root@jenkins test_job]#ll total 8 -rw-r--r-- 1 root root 7 Oct 1 20:32 a.txt -rw-r--r-- 1 root root 3634 Oct 1 20:32 blog_state.txt -rw-r--r-- 1 root root 0 Oct 1 21:12 c.txt drwxr-xr-x 2 root root 6 Oct 1 20:32 myblog drwxr-xr-x 2 root root 6 Oct 1 20:32 myblog-deploy [root@jenkins test_job]#sonar-scanner \\ -Dsonar.projectKey=html \\ -Dsonar.sources=. \\ -Dsonar.host.url=http://11.0.1.202:9000 \\ -Dsonar.login=a3bd8e829343edabbbc58fefe0d3e97fdb12268b 실행 성공:\n서버 확인: 업로드 성공\n","date":"2026-10-02","description":"","objectID":"/ko/posts/sonarqube-practice/","readingTime":5,"tags":["DevOps","SonarQube","CI/CD"],"title":"SonarQube 실전: 사설 배포, 토큰 설정, 코드 품질 스캔","url":"/ko/posts/sonarqube-practice/","wordCount":950},{"categories":["DevOps"],"content":"01. Jenkins 소개 공식 사이트: jenkins.io\nJenkins 은 오픈소스 프로젝트로, Java 기반의 지속적 통합(CI) 도구다. 반복 작업을 모니터링하고 개방적이고 사용하기 쉬운 소프트웨어 플랫폼을 제공하여 소프트웨어 CI를 가능하게 한다。\n02. Jenkins 배포 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 I. 환경 준비 1、OS: CentOS 7.9 2、메모리: 실습 2G, 운영 최소 6G 3、방화벽 비활성화 및 SELinux 끄기 4、JDK17 II. JDK 설치 RPM 패키지를 업로드 [root@jenkins ~]#ll total 178212 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm [root@jenkins ~]#rpm -ivh jdk-17_linux-x64_bin.rpm warning: jdk-17_linux-x64_bin.rpm: Header V3 RSA/SHA256 Signature, key ID ad986da3: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jdk-17-2000:17.0.11-7 ################################# [100%] 설치 성공 여부 확인: [root@jenkins ~]# rpm -qa|grep jdk jdk-17-17.0.11-7.x86_64 II. Jenkins 설치 Jenkins 패키지를 업로드 [root@jenkins ~]#ll total 269432 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm -rw-r--r-- 1 root root 93405530 Oct 1 19:06 jenkins-2.405-1.1.noarch.rpm [root@jenkins ~]#rpm -ivh jenkins-2.405-1.1.noarch.rpm warning: jenkins-2.405-1.1.noarch.rpm: Header V4 RSA/SHA512 Signature, key ID ef5975ca: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jenkins-2.405-1.1 ################################# [100%] III. Jenkins 실행 사용자를 root 로 변경 1) 설정 파일의 실행 사용자 변경 [root@jenkins ~]# vim /etc/sysconfig/jenkins ... JENKINS_USER=\u0026#34;root\u0026#34; ... 2) 시작 설정의 사용자 변경 [root@jenkins ~]# vim /usr/lib/systemd/system/jenkins.service ... User=root Group=root ... [root@jenkins ~]# systemctl daemon-reload 3) 폰트 설정 설치 [root@jenkins ~]# yum -y install fontconfig IV. Jenkins 시작 및 부팅 시 자동 시작 설정 [root@jenkins ~]# systemctl start jenkins [root@jenkins ~]# systemctl enable jenkins 03. Jenkins 로그인 04. 비밀번호 변경 비밀번호 변경 후 재로그인, 사용자 이름은 admin\n05. 플러그인 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1. 플러그인을 Jenkins 홈 디렉터리에 업로드 [root@jenkins ~]# cd /var/lib/jenkins/ [root@jenkins ~]#ll total 576228 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm -rw-r--r-- 1 root root 93405530 Oct 1 19:06 jenkins-2.405-1.1.noarch.rpm -rw-r--r-- 1 root root 314156543 Oct 1 19:30 jenkins_plu.tar.gz 2. 플러그인을 plugins 디렉터리에 압축 해제 [root@jenkins jenkins]#tar xf /root/jenkins_plu.tar.gz -C plugins/ 3. Jenkins 재시작하여 적용 [root@jenkins ~]# systemctl restart jenkins 06. 프로젝트 생성 프리스타일 프로젝트를 만들고 pwd 를 실행하여 Jenkins 가 사용하는 기본 작업 디렉터리 확인\n참고: Jenkins 가 프로젝트를 만들면 홈 /var/lib/jenkins/ 아래에 workspace 디렉터리를 자동으로 만들고, 그 안에 프로젝트 이름의 디렉터리를 생성한다.\n이후 Jenkins 가 GitLab 에서 가져온 코드는 이 프로젝트 디렉터리에 저장된다.\n1 2 [root@jenkins ~]#ls /var/lib/jenkins/workspace/ test_job GitLab 과 Jenkins 연결\n연결 전에는 연결 불가 메시지가 표시됨:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # 키 인증(키리스) 설정 [root@jenkins ~]#ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory \u0026#39;/root/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:Rr4ouinx/cPqVxJsxju2Cjd1mhWmWSuKGOKG532uTvk root@jenkins The key\u0026#39;s randomart image is: +---[RSA 2048]----+ | | | | | o = | | *B o | |o o=oS | |=o . +=O.. | |o=+.Bo+=. | |oo.B.+* | | .=+BOE. | +----[SHA256]-----+ [root@jenkins ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+kaj/NEwHKaIErMEClVYuG3JuWr9CD7XpuO5YZ6JftMmNdAPMrvS0mWqP4SDyT6/le0j1RGkA48GsirESfMqXKvzSSd5AuhIPmUADtGPNK6VSeiMKyoWgiNBFLn6RIOAcXI0MuZrYQMLJz+kOYr6kF9em/6MfFQrLkt9DzIlnR9b7CMLyDnPOZ8FG640DqqX0sZ8G+i2v41VX/c7RGIWAgEhJY+kiMS5cDxWlTf2k8hoZmh0VIa/G9AwnvbklyUiE4ACd9GtEJG5xXJzedn7q0/Ygfr26T29t+KST3SlyrXIaEbEC5WwIMymCEJDCPPCGzqLjsWa+4dbJhoEM1afV root@jenkins # Jenkins 에 git 설치 [root@jenkins ~]#yum install -y git GitLab 에 Jenkins 가 생성한 키 추가:\nJenkins 에서 한 번 git clone 하여 최초 yes 입력 완료\n1 2 3 4 5 6 7 8 9 10 11 12 13 [root@jenkins ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. remote: Enumerating objects: 20, done. remote: Counting objects: 100% (9/9), done. remote: Compressing objects: 100% (8/8), done. remote: Total 20 (delta 0), reused 0 (delta 0), pack-reused 11 Receiving objects: 100% (20/20), done. Resolving deltas: 100% (2/2), done. 이제 연결됨\n다시 빌드하여 풀 테스트\n풀 성공\n웹훅 설정\n웹훅을 설정하여 GitLab 코드를 Jenkins 가 자동으로 풀하고 빌드하도록 구성\n양쪽 저장 후 테스트\n프로젝트 수정 후 다시 푸시하여 테스트\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [root@gitlab blog]#git branch * master [root@gitlab blog]#touch c.txt [root@gitlab blog]#git add . [root@gitlab blog]#git commit -m \u0026#34;test_v2\u0026#34; [master 7b1f29e] test_v2 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 c.txt [root@gitlab blog]#git pull origin master remote: Enumerating objects: 17, done. remote: Counting objects: 100% (9/9), done. remote: Compressing objects: 100% (8/8), done. remote: Total 17 (delta 0), reused 0 (delta 0), pack-reused 8 Unpacking objects: 100% (17/17), done. From 11.0.1.200:dawn/blog * branch master -\u0026gt; FETCH_HEAD Merge made by the \u0026#39;recursive\u0026#39; strategy. a.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 a.txt [root@gitlab blog]#git push -u origin master Counting objects: 7, done. Delta compression using up to 2 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (5/5), 469 bytes | 0 bytes/s, done. Total 5 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git 47f606c..91e80a5 master -\u0026gt; master Branch master set up to track remote branch master from origin. 자동 빌드 성공\n","date":"2026-10-01","description":"","objectID":"/ko/posts/jenkins-practice/","readingTime":5,"tags":["DevOps","Jenkins","CI/CD"],"title":"Jenkins 실전: 사설 배포, 플러그인 설치, GitLab 연동 CI","url":"/ko/posts/jenkins-practice/","wordCount":887},{"categories":["DevOps"],"content":"01. GitLab 관련 사이트 공식 사이트: https://about.gitlab.com/\n국내 미러: https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/\n02. GitLab 배포 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 I. 환경 준비 1、OS: CentOS 7.9 2、메모리: 실습 4G, 운영 최소 8G 3、방화벽 비활성화 및 SELinux 끄기 II. GitLab RPM 패키지 다운로드 [root@gitlab ~]# wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm III. GitLab 설치 # 설치 중 의존성 설치가 필요함 [root@gitlab ~]# rpm -ivh gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm warning: gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID f27eab47: NOKEY error: Failed dependencies: policycoreutils-python is needed by gitlab-ce-16.9.8-ce.0.el7.x86_64 # 의존성 설치 [root@gitlab ~]# yum -y install policycoreutils-python # 의존성 설치 후 GitLab 설치 계속 [root@gitlab ~]# rpm -ivh gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm warning: gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID f27eab47: NOKEY Preparing... ################################# [100%] Updating / installing... 1:gitlab-ce-16.9.8-ce.0.el7 ################################# [100%] IV. 시작 전 도메인 또는 IP 접근 설정 [root@gitlab ~]# vim /etc/gitlab/gitlab.rb ... external_url \u0026#39;http://11.0.1.200\u0026#39; ... 설정 후 다음 명령 실행: [root@gitlab ~]# gitlab-ctl reconfigure 03. GitLab 구성 요소 1 2 3 4 5 6 7 nginx： 정적 웹 서버 gitlab-workhorse: 경량 리버스 프록시 logrotate： 로그 파일 관리 도구 postgresql： 데이터베이스 redis： 캐시 데이터베이스 sidekiq： 백그라운드 큐 작업 실행(비동기). (Ruby) unicorn: Rack 애플리케이션용 HTTP 서버이며, GitLab Rails 앱이 이 서버에서 호스팅됨. (Ruby 웹 서버, 주로 Ruby로 작성) 04. GitLab 자주 쓰는 명령어 1 2 3 4 5 gitlab-ctl start # GitLab 시작 (gitlab-ctl reconfigure 후 서비스 자동 시작) gitlab-ctl status # GitLab 전체 서비스 운영 상태 확인 gitlab-ctl stop # GitLab 서비스 정지 gitlab-ctl stop nginx # 개별 서비스 정지 gitlab-ctl tail # 전체 서비스 로그 보기 05. GitLab 웹 설정 GitLab 로그인: 11.0.1.200\n1. 로그인 비밀번호 gitlab-ctl reconfigure 완료 후 비밀번호 저장 위치를 안내함:\n2. UI 언어를 중국어로 설정 3. 기본 비밀번호 변경 4. 웹훅용 로컬 네트워크 요청 허용 06. GitLab 실전 1. 그룹 만들기 2. 그룹 아래 프로젝트 생성 첫 번째 코드 저장소가 만들어졌지만 비어 있음. 이어서 코드 업로드\n3. 프로젝트에 코드 커밋 프로젝트 생성 후 blog 코드를 이 프로젝트에 업로드해야 함\n서버와 코드 저장소 연결 방식은 두 가지\n첫째: 사용자 이름과 비밀번호로 연결\n둘째: SSH 키 인증(키리스) 방식으로 연결\n우리는 키 인증 방식 사용: 편리하고 보안성이 높음\n키 페어 생성(여기서는 GitLab 서버를 직접 사용) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [root@gitlab ~]#ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory \u0026#39;/root/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:j3FozajJdufxM0ZRtgPa2ZNSwWKvo8bvxbMJl8F5V3M root@gitlab The key\u0026#39;s randomart image is: +---[RSA 2048]----+ | ... | | + = | | + X +E| | =. =.B.+| | S + ++oo| | . + = +. +.| | = o.+o..* | | . . o+o++ + | | ..++oo | +----[SHA256]-----+ 키 페어 확인: [root@gitlab ~]# ll .ssh/ total 8 -rw------- 1 root root 1679 May 14 15:02 id_rsa # 개인키 -rw-r--r-- 1 root root 393 May 14 15:02 id_rsa.pub # 공개키 [root@gitlab ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJGpCKL64eidZ3I/nAVNCNJwD8yeDqelZP0GUTwlcp6IdKaZ7wUOIRDCtGn5r+YnOCKNwMhUNzb3VCOSVLDVIE0x53dPrCMK4OKLmZ6OtTYFBveGOZWWwdYyGQITx2eP7zOSBxeW3AnEWcya/UZdiZrYXA/d29DWjMjCt5QAxOit0EWSir1Z4i2MkTPuljOmjk6A55XAoem0WJRdg/d8KKIiCdcGrj4adYq2kNJ8oYH/9QcnpASUW/ANhzrALnLIX2WaH/p0T3gisLvk2KDUe3rVKD3XaKDlyLgWIViQFm3Me6L2e4taRFiu3VdsbfFfzjDevfDtLaqKLOqPtaczlF root@gitlab 생성한 공개키를 GitLab UI의 SSH 키 관리 페이지에 붙여넣기 서버와 GitLab UI 간 SSH 키 인증 완료. 이어서 코드를 blog 프로젝트에 푸시\n코드를 푸시하는 방법은 두 가지:\n빈 저장소를 로컬에 클론한 뒤 코드를 복사하고 원격 GitLab 에 커밋 프로젝트 안에서 프로젝트 URL 확인\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 1. 링크를 복사하여 실행 [root@gitlab ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. warning: remote HEAD refers to nonexistent ref, unable to checkout. 2. blog 저장소가 로컬에 내려받아졌는지 확인 [root@gitlab ~]#ll total 1176964 drwxr-xr-x 3 root root 18 Oct 1 14:43 blog -rw-r--r-- 1 root root 84579612 Oct 1 14:35 blog_backup_20261001-1435.tar.gz drwxr-xr-x 3 root root 18 Sep 30 19:14 dawn -rw-r--r-- 1 root root 1120627456 Sep 30 23:12 gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm 3. blog 디렉터리로 이동 [root@gitlab ~]#cd blog/ 4. 코드 압축 해제: [root@gitlab blog]#tar xf /root/blog_backup_20261001-1435.tar.gz -C . 5. 코드를 로컬 저장소에 커밋 [root@gitlab blog]#git add . [root@gitlab blog]#git commit -m \u0026#34;DAWN’s Blog\u0026#34; [master (root-commit) 5af4246] DAWN’s Blog 3 files changed, 112 insertions(+) create mode 100644 blog_state.txt create mode 160000 myblog create mode 160000 myblog-deploy 6. 코드를 원격 GitLab 의 blog 저장소에 푸시 GitLab 저장소를 직접 클론했으므로 로컬 디렉터리에 원격 blog 프로젝트 주소가 이미 있음 원격 저장소 확인: [root@gitlab blog]#git remote -v origin git@11.0.1.200:dawn/blog.git (fetch) origin git@11.0.1.200:dawn/blog.git (push) 로컬 master 를 원격 origin 에 푸시 [root@gitlab blog]#git push -u origin master Counting objects: 3, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 1.23 KiB | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git * [new branch] master -\u0026gt; master Branch master set up to track remote branch master from origin. blog 프로젝트 확인, 코드 업로드 성공\n로컬 Git 저장소를 원격 GitLab 에 푸시 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 1. 로컬 저장소 구성 [root@gitlab ~]#mkdir dawn [root@gitlab ~]#cd dawn 2. 저장소 초기화 [root@gitlab dawn]#git init 3. 코드 업로드 [root@gitlab dawn]#touch a.txt [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;test_v1\u0026#34; [master (root-commit) 815c600] test_v1 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 a.txt 4. GitLab 의 blog 저장소를 로컬 원격으로 설정 [root@gitlab dawn]#git remote add origin git@11.0.1.200:dawn/blog.git 5. 로컬 내용을 원격에 푸시(원격에 이미 내용이 있어 바로 푸시하면 오류; 원격 내용을 보존하려면 먼저 pull 병합 후 푸시) [root@gitlab dawn]#git pull origin master warning: no common commits remote: Enumerating objects: 3, done. remote: Counting objects: 100% (3/3), done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), done. From 11.0.1.200:dawn/blog * branch master -\u0026gt; FETCH_HEAD Merge made by the \u0026#39;recursive\u0026#39; strategy. blog_state.txt | 110 ++++++++++++++++++++++++++++++++++++ myblog | 1 + myblog-deploy | 1 + 3 files changed, 112 insertions(+) create mode 100644 blog_state.txt create mode 160000 myblog create mode 160000 myblog-deploy [root@gitlab dawn]#git push -u origin master Counting objects: 6, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (5/5), 462 bytes | 0 bytes/s, done. Total 5 (delta 1), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git 5af4246..0242e0a master -\u0026gt; master Branch master set up to track remote branch master from origin. 일반 계정 dev 생성 생성 후 dev 비밀번호 변경 — 로그아웃하지 말고 사용자 화면의 편집 클릭\n이후 dev 로 GitLab 로그인(첫 로그인 시 비밀번호 변경 필요)\ndev 사용자를 dawn 그룹에 추가 dev 서버에서 키 페어 생성 1 [root@lb01 ~]#ssh-keygen 생성한 공개키를 dev 계정의 SSH 키 페이지에 복사 코드를 dev 호스트 11.0.1.5 에 클론 1 2 3 4 5 6 7 8 9 10 11 12 13 [root@lb01 ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. remote: Enumerating objects: 8, done. remote: Counting objects: 100% (8/8), done. remote: Compressing objects: 100% (6/6), done. remote: Total 8 (delta 1), reused 0 (delta 0), pack-reused 0 Receiving objects: 100% (8/8), done. Resolving deltas: 100% (1/1), done. 코드 수정 후 원격 저장소에 업로드 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 11.0.1.5 의 사용자 설정 [root@lb01 blog]#git config --global user.email \u0026#34;dev@mail.com\u0026#34; [root@lb01 blog]#git config --global user.name \u0026#34;dev\u0026#34; [root@lb01 blog]#vim a.txt 123 [root@lb01 blog]#git commit -am \u0026#34;123\u0026#34; 원격 저장소에 커밋 [root@lb01 blog]#git push -u origin master Counting objects: 5, done. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 257 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: GitLab: You are not allowed to push code to protected branches on this project.To git@11.0.1.200:dawn/blog.git ! [remote rejected] master -\u0026gt; master (pre-receive hook declined) error: failed to push some refs to \u0026#39;git@11.0.1.200:dawn/blog.git\u0026#39; # master 브랜치로의 푸시는 허용되지 않음 다른 브랜치에 푸시: 1. dev 브랜치 생성 [root@lb01 blog]#git branch dev [root@lb01 blog]#git branch dev * master 2. dev 브랜치를 원격에 푸시 [root@lb01 blog]#git push -u origin dev 병합 요청(MR)\ndev 계정에서 병합 요청 생성 root 계정에서 병합 승인 후 원본 dev 브랜치 삭제 웹 서버에서 코드 다운로드 1 2 [root@web01 ~]#git clone http://11.0.1.200/dawn/blog.git 사용자 이름과 비밀번호 입력 ","date":"2026-10-01","description":"","objectID":"/ko/posts/gitlab-practice/","readingTime":8,"tags":["DevOps","GitLab","CI/CD"],"title":"GitLab 실전: 사설 배포, SSH 키 인증, 저장소 관리","url":"/ko/posts/gitlab-practice/","wordCount":1499},{"categories":["DevOps"],"content":"01. Git 설치 및 저장소 초기화 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [root@gitlab ~]#yum install -y git [root@gitlab ~]#git --version git version 1.8.3.1 [root@gitlab ~]#git config --global user.name \u0026#34;dawn\u0026#34; [root@gitlab ~]#git config --global user.email \u0026#34;dawn@mail.com\u0026#34; [root@gitlab ~]#git config --global color.ui true [root@gitlab ~]#cat .gitconfig [user] name = dawn email = dawn@mail.com [color] ui = true [root@gitlab ~]#mkdir dawn [root@gitlab ~]#cd dawn [root@gitlab dawn]#ll total 0 [root@gitlab dawn]#git init Initialized empty Git repository in /root/dawn/.git/ [root@gitlab dawn]#ll total 0 [root@gitlab dawn]#ll -a total 0 drwxr-xr-x 3 root root 18 Jun 11 10:57 . dr-xr-x---. 4 root root 174 Jun 11 10:57 .. drwxr-xr-x 7 root root 119 Jun 11 10:57 .git 02. Git 명령어 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 1. 저장소 상태 확인하기 (항상 작업 트리를 깨끗하게 유지) [root@gitlab dawn]#git status # On branch master # # Initial commit # nothing to commit (create/copy files and use \u0026#34;git add\u0026#34; to track) 2. 일반 파일 생성하기 [root@gitlab dawn]#touch a.txt 3. 작업 트리의 내용을 스테이징하기 [root@gitlab dawn]#git add a.txt # git add . 는 작업 트리의 모든 내용을 스테이징 영역에 올린다 [root@gitlab dawn]#git add . 4. 스테이징 영역의 내용 삭제하기 [root@gitlab dawn]#git rm --cached a.txt rm \u0026#39;a.txt\u0026#39; 5. 스테이징 후 로컬 저장소에 커밋하기 [root@gitlab dawn]#git add a.txt [root@gitlab dawn]#git status # On branch master # # Initial commit # # Changes to be committed: # (use \u0026#34;git rm --cached \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # new file: a.txt # # git commit -m 뒤에 메시지를 붙여 로컬 저장소에 커밋한다 [root@gitlab dawn]#git commit -m \u0026#34;create a.txt\u0026#34; [master (root-commit) b65d348] create a.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 a.txt 상태 확인: [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 파일 생성 touch a.txt 스테이징 git add a.txt 로컬 저장소에 커밋 git commit -m \u0026#34;xxx\u0026#34; 2. git 으로 삭제하기 [root@gitlab dawn]#git rm a.txt rm \u0026#39;a.txt\u0026#39; [root@gitlab dawn]#git status # On branch master # Changes to be committed: # (use \u0026#34;git reset HEAD \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # deleted: a.txt # [root@gitlab dawn]#git commit -m \u0026#34;del a.txt\u0026#34; [master 41fc71b] del a.txt 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 a.txt [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 참고: 작업 트리 파일을 스테이징한 뒤 취소하려면 스테이징 영역에서 제거한다 [root@gitlab dawn]#touch c.txt [root@gitlab dawn]#git add c.txt [root@gitlab dawn]#git rm --cached c.txt rm \u0026#39;c.txt\u0026#39; 3. 작업 트리 내용 복구하기 [root@gitlab dawn]#git reset HEAD b.txt [root@gitlab dawn]#git checkout -- b.txt 4. git diff 로 비교하기 git diff 작업 트리와 스테이징 영역의 차이를 비교한다 [root@gitlab dawn]#git diff diff --git a/b.txt b/b.txt index e69de29..ccc3e7b 100644 --- a/b.txt +++ b/b.txt @@ -0,0 +1 @@ +aaaaa git diff --cached 스테이징 영역과 로컬 저장소의 차이를 비교한다 [root@gitlab dawn]#git diff --cached diff --git a/b.txt b/b.txt index e69de29..ccc3e7b 100644 --- a/b.txt +++ b/b.txt @@ -0,0 +1 @@ +aaaaa 5. 파일 이름 변경하기 [root@gitlab dawn]#git mv b.txt b.bak [root@gitlab dawn]#git status # On branch master # Changes to be committed: # (use \u0026#34;git reset HEAD \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # renamed: b.txt -\u0026gt; b.bak # [root@gitlab dawn]#git commit -m \u0026#34;newname b.txt--\u0026gt;b.bak\u0026#34; [master 55335a7] newname b.txt--\u0026gt;b.bak 1 file changed, 0 insertions(+), 0 deletions(-) rename b.txt =\u0026gt; b.bak (100%) [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 6. 특정 스냅샷으로 롤백하기 모든 스냅샷 확인: [root@gitlab dawn]#git log --oneline ae6585c xx 55335a7 newname b.txt--\u0026gt;b.bak 09ffe53 modifile b.txt 043a297 newfile b.txt 41fc71b del a.txt b65d348 create a.txt [root@gitlab dawn]#ll total 8 -rw-r--r-- 1 root root 6 Jun 11 12:10 b.bak -rw-r--r-- 1 root root 6 Jun 11 12:07 b.txt git reset --hard 로 롤백하기 [root@gitlab dawn]#git reset --hard b65d348 HEAD is now at b65d348 create a.txt [root@gitlab dawn]#ll total 0 -rw-r--r-- 1 root root 0 Jun 11 12:12 a.txt 7. 지금까지의 모든 커밋 작업 확인: [root@gitlab dawn]#git reflog b65d348 HEAD@{0}: reset: moving to b65d348 ae6585c HEAD@{1}: commit: xx 55335a7 HEAD@{2}: commit: newname b.txt--\u0026gt;b.bak 09ffe53 HEAD@{3}: commit: modifile b.txt 043a297 HEAD@{4}: commit: newfile b.txt 41fc71b HEAD@{5}: commit: del a.txt b65d348 HEAD@{6}: commit (initial): create a.txt [root@gitlab dawn]#git reset --hard ae6585c HEAD is now at ae6585c xx [root@gitlab dawn]#ll total 8 -rw-r--r-- 1 root root 6 Jun 11 12:16 b.bak -rw-r--r-- 1 root root 6 Jun 11 12:16 b.txt Git 브랜치 1. 브랜치 목록 확인하기 (* 는 현재 브랜치) [root@gitlab dawn]#git branch * master 2. 브랜치 생성하기 [root@gitlab dawn]#git branch dev [root@gitlab dawn]#git branch dev * master 3. dev 브랜치로 전환하기 [root@gitlab dawn]#git checkout dev Switched to branch \u0026#39;dev\u0026#39; [root@gitlab dawn]#git branch * dev master 4. 브랜치에서 파일을 만들고 로컬 저장소에 커밋하기 [root@gitlab dawn]#touch aa.txt [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;newfile aa.txt\u0026#34; [dev 2694c7f] newfile aa.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 aa.txt 5. 브랜치 병합하기 먼저 master 브랜치로 되돌아간다 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master dev 브랜치를 master 에 병합한다 [root@gitlab dawn]#git merge dev Updating ae6585c..2694c7f Fast-forward aa.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 aa.txt 6. 기능 구현이 끝나면 브랜치를 즉시 삭제한다 (원격 브랜치에 뒤처지기 때문) [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 2694c7f). [root@gitlab dawn]#git branch * master 7. 충돌 병합 ---------------------------------- 파일 내용을 수정했다고 해서 병합이 막히는 것은 아니다 master 브랜치의 파일을 수정해 bbbbb 를 추가한다 [root@gitlab dawn]#vim b.txt aaaaa bbbbb 한 번의 명령으로 dev 브랜치 생성과 전환을 동시에 할 수 있다 [root@gitlab dawn]#git checkout -b dev Switched to a new branch \u0026#39;dev\u0026#39; [root@gitlab dawn]# [root@gitlab dawn]# [root@gitlab dawn]#git branch * dev master b.txt 의 내용을 수정한다 [root@gitlab dawn]#vim b.txt aaaaa bbccc 로컬 저장소에 커밋하기 [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;modifle bbbccc\u0026#34; master 브랜치로 돌아가 dev 브랜치를 병합한다 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master [root@gitlab dawn]#cat b.txt aaaaa bbbbb [root@gitlab dawn]#git merge dev Updating f56faba..dc17f6d Fast-forward b.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) [root@gitlab dawn]#cat b.txt aaaaa bbccc ------------------------------------------------------ 충돌이 발생하는 경우 dev 브랜치 생성하기 [root@gitlab dawn]#git branch dev master 브랜치에서 b.txt 의 3번째 줄을 수정한다 [root@gitlab dawn]#cat b.txt aaaaa iiiii bbbbb [root@gitlab dawn]#git commit -am \u0026#34;modifile b.txt bbbbb\u0026#34; [master ae4ab87] modifile b.txt bbbbb 1 file changed, 1 insertion(+) dev 브랜치로 전환해 b.txt 의 3번째 줄을 수정한다 [root@gitlab dawn]#cat b.txt aaaaa iiiii ccccc 로컬 저장소에 커밋하기 [root@gitlab dawn]#git commit -am \u0026#34;modifile b.txt ccccc\u0026#34; [dev 925c06c] modifile b.txt ccccc 1 file changed, 1 insertion(+) master 브랜치로 전환해 브랜치를 병합한다 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master [root@gitlab dawn]#git merge dev Auto-merging b.txt CONFLICT (content): Merge conflict in b.txt Automatic merge failed; fix conflicts and then commit the result. 파일을 확인하면 충돌 표시가 보인다 [root@gitlab dawn]#cat b.txt aaaaa iiiii \u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt; HEAD bbbbb ======= ccccc \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; dev 수동으로 충돌을 해결해 한쪽만 남기거나 모두 남긴다 [root@gitlab dawn]#vim b.txt aaaaa iiiii bbbbb ccccc [root@gitlab dawn]#git commit -am \u0026#34;merge dev\u0026#34; [master 325eef2] merge dev [root@gitlab dawn]# [root@gitlab dawn]# [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean [root@gitlab dawn]#cat b.txt aaaaa iiiii bbbbb ccccc 병합 후 dev 브랜치를 삭제한다 [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 925c06c). ","date":"2026-10-01","description":"","objectID":"/ko/posts/git-practice-notes/","readingTime":8,"tags":["DevOps","Git","버전 관리"],"title":"Git 실전 노트: 설치부터 기본 명령어와 브랜치 관리까지","url":"/ko/posts/git-practice-notes/","wordCount":1495},{"categories":["Zabbix","모니터링・운영"],"content":"1. 로그인 사용자 수 확인하기 1 2 3 4 5 6 7 [root@web02 ~]#w 17:44:18 up 1:17, 2 users, load average: 0.01, 0.02, 0.05 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root pts/0 11.0.1.1 16:31 11:46 0.07s 0.07s -bash root pts/1 11.0.1.52 17:07 2.00s 0.04s 0.00s w [root@web02 ~]#w|awk \u0026#39;NR==1{print $4}\u0026#39; 2 2. Zabbix 모니터링 항목으로 정의하기 1 2 3 4 5 6 7 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; # zabbix_agentd -p 로 항목 문법 검사가 가능합니다 [root@web02 ~]#zabbix_agentd -p|grep login_number login_number [t|2] # 에이전트 재시작 [root@web02 ~]# systemctl restart zabbix-agent.service 3. Zabbix 서버 측에서 수집·모니터링하기 서버에서 명령줄로 모니터링 항목을 통해 사용자 정의 값을 가져올 수 있는지 테스트합니다.\n1 2 3 4 # zabbix-get 명령 설치 [root@zabbix ~]# yum -y install zabbix-get [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k login_number 2 웹 페이지에서 모니터링 항목 추가\n모니터링 항목 확인\n4. TCP 22 포트 생존 여부 모니터링 모니터링하려는 정보에 맞는 키 값을 선택합니다.\nTCP 포트가 LISTEN 상태인지 확인합니다. 반환값: 0 - 대기 안 함; 1 - 대기 중.\nweb02의 sshd 서비스 중지\n1 [root@web02 ~]# systemctl stop sshd 5. 표시용 커스텀 값 매핑 구성 값 매핑 적용\n6. 서버 메모리 비율 커스텀 모니터링 1단계: 커스텀 항목을 정의하여 가용률 가져오기\n1 2 3 4 5 6 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; UserParameter=mem_free,free|awk \u0026#39;NR==2{print $NF/$2*100}\u0026#39; [root@web02 ~]# systemctl restart zabbix-agent.service [root@web02 ~]#zabbix_agentd -p|grep mem_free mem_free [t|79.8753] 2단계: 서버에서 키 값 가져오기를 테스트하고 항목 추가\n1 2 [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k mem_free 79.9775 7. 가용 메모리에 트리거 설정 가용 메모리 비율이 20% 미만이면 알림을 발보합니다.\n테스트: swap 파티션 끄기\n1 [root@web02 ~]# swapoff -a dd로 부하 테스트를 실행해 메모리 소비\n1 [root@web02 ~]# dd if=/dev/zero of=/dev/null bs=1200M count=2048 8. 다중 조건 트리거 구성 두 조건이 동시에 만족할 때만 알림을 발보합니다.\nswap 가용률 추가\n1 2 3 4 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; UserParameter=mem_free,free|awk \u0026#39;NR==2{print $NF/$2*100}\u0026#39; UserParameter=swap_free,free -m|awk \u0026#39;NR==3{print $3/$2*100}\u0026#39; 트리거 설정 수정\n이후 dd 부하 테스트를 다시 실행해 결과를 확인합니다.\n","date":"2026-09-23","description":"","objectID":"/ko/posts/zabbix-custom-monitor/","readingTime":2,"tags":["Zabbix","커스텀모니터링","UserParameter","트리거","값매핑"],"title":"Zabbix 커스텀 모니터링","url":"/ko/posts/zabbix-custom-monitor/","wordCount":322},{"categories":["Zabbix","모니터링・운영"],"content":"기업 Zabbix 고부하 시나리오에서 사용합니다.\n1. 독립 데이터베이스 서버(11.0.1.52) 준비 및 MariaDB 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@db02 ~]#yum -y install mariadb-server [root@db02 ~]#systemctl enable mariadb --now [root@db02 ~]#mysql Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 2 Server version: 5.5.68-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [(none)]\u0026gt; create database zabbix character set utf8 collate utf8_bin; Query OK, 1 row affected (0.02 sec) MariaDB [(none)]\u0026gt; create user \u0026#39;zabbix\u0026#39;@\u0026#39;%\u0026#39; identified by \u0026#39;zabbix\u0026#39;; Query OK, 0 rows affected (0.10 sec) MariaDB [(none)]\u0026gt; grant all privileges on zabbix.* to \u0026#39;zabbix\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) 2. Zabbix 서버에서 zabbix 데이터베이스를 내보내 새 데이터베이스로 가져오기 1 2 [root@zabbix ~]# mysqldump -uroot -B zabbix \u0026gt; zabbix.sql [root@zabbix ~]# cat zabbix.sql |mysql -h 172.16.1.52 -uzabbix -pzabbix zabbix 3. Web UI의 데이터베이스 연결 설정 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@zabbix ~]# systemctl stop mariadb [root@zabbix ~]# cat /etc/zabbix/web/zabbix.conf.php \u0026lt;?php // Zabbix GUI configuration file. global $DB; $DB[\u0026#39;TYPE\u0026#39;] = \u0026#39;MYSQL\u0026#39;; $DB[\u0026#39;SERVER\u0026#39;] = \u0026#39;172.16.1.52\u0026#39;; $DB[\u0026#39;PORT\u0026#39;] = \u0026#39;0\u0026#39;; $DB[\u0026#39;DATABASE\u0026#39;] = \u0026#39;zabbix\u0026#39;; $DB[\u0026#39;USER\u0026#39;] = \u0026#39;zabbix\u0026#39;; $DB[\u0026#39;PASSWORD\u0026#39;] = \u0026#39;zabbix\u0026#39;; [root@zabbix ~]# grep ^DB /etc/zabbix/zabbix_server.conf DBHost=172.16.1.52 DBName=zabbix DBUser=zabbix DBPassword=zabbix 4. 서비스 재시작 1 [root@zabbix ~]# systemctl restart zabbix-server httpd ","date":"2026-09-23","description":"","objectID":"/ko/posts/zabbix-split-database/","readingTime":2,"tags":["Zabbix","데이터베이스","분리","고부하"],"title":"Zabbix 데이터베이스 분리","url":"/ko/posts/zabbix-split-database/","wordCount":243},{"categories":["Zabbix","모니터링・운영"],"content":"1. zabbix-agent 클라이언트 설치 — 서버(11.0.1.8) 준비 1 2 3 4 5 6 7 8 저장소를 설정합니다: [root@web02 ~]# rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 서버의 yum 저장소를 클라이언트로 동기화합니다: [root@zabbix ~]#scp /etc/yum.repos.d/CentOS-SCLo-scl.repo root@172.16.1.8:/etc/yum.repos.d/CentOS-SCLo-scl.repo [root@zabbix ~]#scp /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo root@172.16.1.8:/etc/yum.repos.d/CentOS-SCLo-scl-rh.repo [root@zabbix ~]#scp /etc/yum.repos.d/zabbix.repo root@172.16.1.8:/etc/yum.repos.d/zabbix.repo 설치 시 패키지 충돌이 발생하면: [root@web02 ~]# yum -y install centos-release-scl 2. zabbix-agent에 서버 정보를 설정하고 시작 1 2 3 4 5 6 [root@web02 ~]# yum -y install zabbix-agent [root@web02 ~]# vim /etc/zabbix/zabbix_agentd.conf Server=172.16.1.71 ServerActive=172.16.1.71 Hostname=web02 [root@web02 ~]# systemctl enable zabbix-agent --now 3. Zabbix에서 web02 모니터링 4. web02 모니터링 설정 완료 ","date":"2026-09-23","description":"","objectID":"/ko/posts/zabbix-monitor-server/","readingTime":1,"tags":["Zabbix","모니터링","zabbix-agent","모니터링설정"],"title":"Zabbix로 서버 정보 모니터링하기","url":"/ko/posts/zabbix-monitor-server/","wordCount":90},{"categories":["Zabbix","모니터링・운영"],"content":"1. 저장소 설정 1 rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 2. zabbix-server 및 zabbix-agent 설치 1 yum -y install zabbix-server-mysql zabbix-agent 3. SCL 저장소 설치 (버전 충돌 해결) 1 [root@zabbix ~]# yum -y install centos-release-scl 4. 프론트엔드 저장소 활성화 1 2 3 4 5 [root@zabbix ~]# vim /etc/yum.repos.d/zabbix.repo [zabbix-frontend] ... enabled=1 ... 5. CentOS-SCLo-scl.repo / CentOS-SCLo-scl-rh.repo 의 깨진 mirrorlist를 Aliyun baseurl로 교체 1 2 3 4 5 6 7 8 9 10 [root@zabbix yum.repos.d]#vim CentOS-SCLo-scl-rh.repo [centos-sclo-rh] ... baseurl=https://mirrors.aliyun.com/centos/7/sclo/x86_64/rh ... [root@zabbix yum.repos.d]#vim CentOS-SCLo-scl.repo [centos-sclo-sclo] ... baseurl=https://mirrors.aliyun.com/centos/7/sclo/x86_64/sclo ... 6. PHP/Apache 프론트엔드 설치 1 [root@zabbix ~]# yum -y install zabbix-web-mysql-scl zabbix-apache-conf-scl 7. MariaDB 데이터베이스 설치 1 2 3 [root@zabbix ~]# yum -y install mariadb-server 데이터베이스를 시작합니다: [root@zabbix ~]#systemctl enable mariadb --now 8. Zabbix 데이터베이스 생성 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@zabbix ~]#mysql -uroot Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 7 Server version: 5.5.68-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [(none)]\u0026gt; create database zabbix character set utf8 collate utf8_bin; Query OK, 1 row affected (0.00 sec) MariaDB [(none)]\u0026gt; create user zabbix@localhost identified by \u0026#39;zabbix\u0026#39;; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; grant all privileges on zabbix.* to zabbix@localhost; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; set global log_bin_trust_function_creators = 1; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; quit Bye 9. 데이터베이스 초기화 (Zabbix 구동 전 필요한 스키마 가져오기) 1 2 3 4 5 [root@zabbix ~]# zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix # zabbix 비밀번호 입력 프롬프트가 나타납니다 [root@zabbix ~]# mysql -uroot MariaDB [(none)]\u0026gt; set global log_bin_trust_function_creators = 0; quit 10. Zabbix 데이터베이스 연결 설정 1 2 3 4 [root@zabbix ~]# egrep ^DB /etc/zabbix/zabbix_server.conf DBName=zabbix DBUser=zabbix DBPassword=zabbix # 주석을 해제하고 비밀번호를 설정 11. 시간대 설정 1 2 [root@zabbix ~]# tail -1 /etc/opt/rh/rh-php72/php-fpm.d/zabbix.conf php_value[date.timezone] = Asia/Shanghai 12. 서비스(server, agent, httpd, php) 시작 및 부팅 시 자동 시작 설정 1 [root@zabbix ~]# systemctl enable zabbix-server zabbix-agent httpd rh-php72-php-fpm --now 13. 11.0.1.71/zabbix 에서 Zabbix 웹 UI 접속 14. Zabbix 설치 완료 15. 중국어로 전환하고 폰트 재설정 Windows 폰트를 찾아 Zabbix의 폰트 디렉터리에 복사합니다.\nWindows 폰트 저장 위치:\n폰트 중 하나를 업로드한 뒤 이름을 변경합니다:\n1 2 3 4 5 6 7 8 9 10 [root@zabbix ~]#cd /usr/share/zabbix/assets/fonts/ [root@zabbix fonts]#ls graphfont.ttf simhei.ttf # 업로드한 폰트 [root@zabbix fonts]#rm -f graphfont.ttf # 기존 폰트 삭제 [root@zabbix fonts]#ln -s /usr/share/zabbix/assets/fonts/simhei.ttf graphfont.ttf # 업로드한 폰트에 연결 [root@zabbix fonts]#chmod 644 simhei.ttf [root@zabbix fonts]#ll total 9520 lrwxrwxrwx 1 root root 41 Sep 23 01:19 graphfont.ttf -\u0026gt; /usr/share/zabbix/assets/fonts/simhei.ttf -rw-r--r-- 1 root root 9745792 Sep 23 01:18 simhei.ttf ","date":"2026-09-23","description":"","objectID":"/ko/posts/zabbix-install/","readingTime":3,"tags":["Zabbix","모니터링","CentOS7","설치"],"title":"Zabbix 설치 및 배포","url":"/ko/posts/zabbix-install/","wordCount":454},{"categories":["Kubernetes","운영・배포"],"content":"계획은 다음과 같습니다:\nMaster 노드: 호스트명: master IP 주소: 192.168.8.30\nWorker01 노드: 호스트명: node1 IP 주소: 192.168.8.31\nWorker02 노드: 호스트명: node2 IP 주소: 192.168.8.32\n세 노드 모두에서 다음 단계를 완료하고, 세 노드의 시간이 일치하는지 확인하세요.\n1. 방화벽 및 SELinux 비활성화 1 2 3 4 [root@master ~]# systemctl disable firewalld.service --now [root@master ~]# vim /etc/selinux/config SELINUX=disabled [root@master ~]# setenforce 0 2. 스왑 비활성화 1 2 [root@master ~]# sed -i \u0026#39;/swap/s/^/#/\u0026#39; /etc/fstab [root@master ~]# swapoff -a 3. 시스템 파라미터 설정 및 커널 모듈 로드 1 2 3 4 5 6 7 8 9 10 11 12 [root@master ~]# cat \u0026gt; /etc/modules-load.d/k8s.conf \u0026lt;\u0026lt; EOF overlay br_netfilter EOF [root@master ~]# modprobe overlay [root@master ~]# modprobe br_netfilter [root@master ~]# cat \u0026gt; /etc/sysctl.d/k8s.conf \u0026lt;\u0026lt; EOF net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF [root@master ~]# sysctl --system 4. 컨테이너 런타임(Containerd) 설치 Kubernetes v1.30에서는 컨테이너 런타임으로 Containerd 사용을 권장합니다：\n1 [root@master ~]# yum -y remove runc OS에 기본 포함된 yum 저장소 파일을 삭제합니다：\n1 [root@master ~]# rm -rf /etc/yum.repos.d/Cent*.repo 로컬 yum 저장소를 구성합니다：\n1 2 3 4 5 6 7 8 9 10 11 12 [root@master ~]# mount /dev/cdrom /media/ [root@master ~]# cat /etc/fstab | grep iso9660 /dev/cdrom\t/media\tiso9660 defaults 0 0 [root@master ~]# cat /etc/yum.repos.d/dvd.repo [BaseOS] name=BaseOS baseurl=file:///media/BaseOS gpgcheck=0 [AppStream] name=AppStream baseurl=file:///media/AppStream gpgcheck=0 Containerd를 구성합니다：\n1 2 [root@master ~]# yum install -y yum-utils [root@master ~]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo containerd.io를 설치합니다：\n1 2 3 4 5 6 7 8 9 10 [root@master ~]# yum install -y containerd.io # 설정 파일을 생성하고 편집합니다 [root@master ~]# mkdir -p /etc/containerd [root@master ~]# containerd config default \u0026gt; /etc/containerd/config.toml # systemd cgroup 드라이버를 사용하도록 설정을 편집합니다 #sed -i \u0026#39;s/SystemdCgroup = false/SystemdCgroup = true/g\u0026#39; /etc/containerd/config.toml # 이미지 가속 구성(선택 사항) #sed -i \u0026#39;s|registry.k8s.io|registry.aliyuncs.com/google_containers|g\u0026#39; /etc/containerd/config.toml # 시작하고 부팅 시 활성화합니다 #systemctl enable --now containerd 5. Kubernetes 설치 및 구성 1 2 3 4 5 6 7 [root@master yum.repos.d]# cat kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm/ enabled=1 gpgcheck=1 gpgkey=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm/repodata/repomd.xml.key 지정한 버전의 Kubernetes 구성 요소를 설치합니다：\n1 2 3 [root@master ~]# yum install -y kubelet-1.30.0 kubeadm-1.30.0 kubectl-1.30.0 --disableexcludes=kubernetes # kubelet 시작 [root@master ~]# systemctl enable --now kubelet 6. Kubernetes 초기화 주의：모든 노드에서 위 구성을 마친 후, 다음 단계는 master 노드에서만 실행하세요.\n1 2 3 4 5 [root@master ~]# kubeadm init --pod-network-cidr=10.244.0.0/16 --service-cidr=172.16.0.0/16 --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.30.0 --control-plane-endpoint=\u0026#34;192.168.8.30:6443\u0026#34; --upload-certs # kubectl 구성 [root@master ~]# mkdir -p $HOME/.kube [root@master ~]# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config [root@master ~]# chown $(id -u):$(id -g) $HOME/.kube/config 7. 워커 노드를 Kubernetes 클러스터에 가입 다음 명령을 워커 노드에서 실행합니다. 주의：노드마다 토큰과 CA 인증서 해시가 다를 수 있으므로 자신의 시스템 프롬프트를 따르세요. 인증서가 만료되었을 수도 있으며, 만료된 경우 kubeadm token create --print-join-command로 가입 명령을 다시 생성하세요.\n1 2 [root@node1 ~]# kubeadm join 192.168.8.30:6443 --token yfjrsi.60lmqgzobp0b8al3 --discovery-token-ca-cert-hash sha256:f4e3c924918545abfd8148e71a377e086d591c6a59856a496b4b33e0987d9c18 [root@node2 ~]# kubeadm join 192.168.8.30:6443 --token yfjrsi.60lmqgzobp0b8al3 --discovery-token-ca-cert-hash sha256:f4e3c924918545abfd8148e71a377e086d591c6a59856a496b4b33e0987d9c18 8. master 노드에 Calico 네트워크 플러그인 설치 1 2 # Calico 네트워크 플러그인 설치 [root@master ~]# kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml 참고：잘 알려진 이유로 Calico 이미지 다운로드에 실패할 수 있습니다.\n해당 매니페스트 파일을 수정해 두었으니, master 노드에 다운로드하여 적용하세요：\n이 파일은 Baidu Netdisk(百度网盘)에 업로드되어 있습니다. 다운로드 링크: https://pan.baidu.com/s/1BPCFxv0kE0KWb04Qsgy71g (추출 코드: s44k)\n1 [root@master ~]# kubectl apply -f /root/calico-ucloud.yaml 설치 완료 후 다음을 실행합니다：\n1 2 3 4 5 [root@master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION master Ready control-plane 65m v1.30.13 node1 Ready \u0026lt;none\u0026gt; 62m v1.30.0 node2 Ready \u0026lt;none\u0026gt; 62m v1.30.0 위 결과가 보이면 Kubernetes 설치가 완료된 것입니다.\n9. 추가 내용 각 노드에서 다음 명령을 실행합니다：\n1 2 3 4 5 kubectl completion bash \u0026gt; /etc/bash_completion.d/kubectl source /etc/bash_completion.d/kubectl vim /root/.bashrc alias docker=\u0026#39;crictl\u0026#39; source /root/.bashrc 참고:\nkubectl 셸 완성: 위의 첫 두 줄은 kubectl 완성 스크립트를 /etc/bash_completion.d/에 기록합니다. 단, 이전에 시스템에 bash-completion 패키지가 설치되어 있어야 합니다(yum install -y bash-completion 또는 apt install -y bash-completion). 설치되어 있지 않으면 해당 디렉터리가 존재하지 않아 완성이 동작하지 않습니다. 또한 kubectl 자체도 이 두 명령을 실행해야 비로소 완성이 활성화됩니다(Kubernetes는 이 추가 설정이 필요합니다). docker 별칭: Kubernetes가 containerd 런타임으로 전환되면서 컨테이너 관리 명령은 docker 대신 crictl을 사용합니다. 여기서 docker를 crictl로 별칭하는 것은 docker 명령에 익숙한 사용자를 배려하여 기존 작업 습관을 그대로 유지할 수 있게 하기 위함입니다. # docker images를 실행했을 때 다음과 같은 오류가 발생하는 경우：\nKubernetes에 다음 경고가 표시되면 해결 방법은 다음과 같습니다.\nWARN[0000] image connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.\n이 경고는 사용 중인 컨테이너 런타임 클라이언트(예： crictl 또는 kubelet)가 기본 엔드포인트 설정을 사용하고 있으며, 이 설정은 이제 더 이상 사용되지 않으므로 명시적으로 지정해야 함을 나타냅니다. 해결 단계는 다음과 같습니다：\n1 2 3 4 5 6 7 cat \u0026gt; /etc/crictl.yaml \u0026lt;\u0026lt; EOF runtime-endpoint: unix:///run/containerd/containerd.sock # 사용하는 컨테이너 런타임에 맞게 선택 image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false pull-image-on-create: false EOF 설명：\nContainerd를 사용하는 경우 엔드포인트는 unix:///run/containerd/containerd.sock입니다.\nCRI-O를 사용하는 경우 엔드포인트는 unix:///run/crio/crio.sock입니다.\n비고：선택 사항.\nKubernetes를 수동으로 설치하면 기본적으로 데이터 전송에 iptables를 사용합니다. 대규모 환경에서는 IPVS 모드로 변경하는 것이 좋습니다. 설치 완료 후 IPVS 모드로 변경하는 단계는 다음과 같습니다：\n1. 모든 노드에 ipvsadm 패키지 설치 2. 모든 노드에서 모듈 로드 1 2 3 4 5 6 7 cat \u0026gt; /etc/modules-load.d/ipvs.conf \u0026lt;\u0026lt; EOF ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack EOF 3. kube-proxy ConfigMap 편집 1 kubectl edit cm kube-proxy -n kube-system 열린 편집기에서 다음 섹션을 찾아 수정합니다：\n1 2 3 4 5 6 7 apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: \u0026#34;ipvs\u0026#34; # \u0026#34;iptables\u0026#34;를 \u0026#34;ipvs\u0026#34;로 변경 ipvs: scheduler: \u0026#34;rr\u0026#34; # 로드 밸런싱 알고리즘： rr|wrr|lc 4. kube-proxy Pod 강제 재시작 모든 kube-proxy Pod를 삭제하여 재생성을 트리거합니다：\n1 #kubectl delete pods -n kube-system -l k8s-app=kube-proxy 5. 새 Pod가 정상 동작하는지 확인 1 2 #kubectl get pods -n kube-system -l k8s-app=kube-proxy # 모든 Pod가 Running 상태여야 합니다 6. IPVS 설정이 적용되었는지 확인 1 2 3 4 5 [root@master ~]# ipvsadm -L -n TCP 192.168.8.30:30080 rr -\u0026gt; 10.244.104.3:80 Masq 1 0 0 -\u0026gt; 10.244.104.4:80 Masq 1 0 0 -\u0026gt; 10.244.166.132:80 Masq 1 0 0 ","date":"2026-08-07","description":"","objectID":"/ko/posts/k8s-v1.30-manual-deploy/","readingTime":5,"tags":["Kubernetes","kubeadm","containerd","calico","v1.30","클러스터배포"],"title":"Kubernetes v1.30 클러스터 수동 배포 (kubeadm 방식)","url":"/ko/posts/k8s-v1.30-manual-deploy/","wordCount":944},{"categories":["가상화"],"content":" 이 글은 KVM 가상화 기술을 체계적으로 소개하며, 발전 역사, 배포 방법, 가상 머신 관리, 네트워크 모드 구성 및 명령줄을 통한 자동 설치를 다룹니다. 모든 명령은 CentOS/RHEL 환경에서 검증되었습니다.\nI. KVM 발전 역사 KVM(Kernel-based Virtual Machine)은 오늘날 클라우드 컴퓨팅 분야의 주류 가상화 기술입니다. 그 발전 과정을 이해하면 업계가 왜 결국 KVM을 선택했는지 알 수 있습니다.\n가상화 기술은 크게 두 가지로 나눌 수 있습니다: 폐쇄형 가상화(예: VMware, Microsoft Hyper-V)와 오픈소스 가상화(예: Xen, KVM).\n초기 오픈소스 가상화의 대표 주자는 Xen이었습니다. AWS와 알리바바 클라우드는 초기에 모두 Xen 방식을 채택했습니다. 시트릭스의 XenServer 및 데스크톱 클라우드 제품인 FusionAccess(Citrix XenDesktop에 대응)도 Xen을 기반으로 했습니다. 화웨이의 FusionCompute도 6.3 버전 이전에는 Xen을 사용했습니다.\n주요 이정표는 다음과 같습니다:\n2008년: Red Hat이 KVM을 인수하고 RHEL 5.4에서 정식으로 KVM을 출시했습니다(RHEL 5.0에는 Xen이 탑재되었습니다). 2010년: OpenStack 프로젝트가 탄생했습니다. Red Hat은 IBM, Dell, VMware, Cisco, HP 등과 함께 KVM 오픈소스 커뮤니티를 포용하고 오픈소스 소프트웨어 재단 아래에서 생태계를 구축했습니다. 같은 시기에 시트릭스는 CloudStack 커뮤니티를 추진했습니다. 2014년: RHEL 5.11이 출시되었습니다(RHEL 5.x 시리즈의 수명 종료, 7년의 기술 지원 제공 및 KVM으로의 무료 업그레이드 가능). 2017년 전후: AWS가 KVM 기반 Nitro 플랫폼 인스턴스를 출시했고, 알리바바 클라우드와 전 세계 수많은 업체가 KVM을 전면 채택했습니다. 오늘날 주요 클라우드 서비스 제공업체의 가상화 기반은 기본적으로 KVM입니다. 화웨이의 FusionCompute 6.3도 Xen에서 KVM으로 전환했습니다. 2019년 IBM이 약 340억 달러에 Red Hat을 인수하면서 Ansible, GlusterFS, Ceph 등 Red Hat 생태계 프로젝트가 그 산하에 편입되었으며, 이는 이 기술 노선의 가치를 추가로 입증합니다.\nII. KVM이란 무엇인가 KVM은 Kernel-based Virtual Machine의 약자입니다. 핵심 특징은 다음과 같습니다:\n커널 업그레이드 불필요: 시스템 자체의 커널을 직접 재사용하며, 각 가상 머신은 하나의 프로세스로 실행됩니다. 경량 가상화 플랫폼: 기존 방식에 비해 더 가볍고 효율적입니다. 통합 관리: Xen/KVM 가상화 플랫폼 모두 통합 관리 플랫폼과 명령줄 도구를 제공합니다. III. KVM 배포 1. KVM 필수 패키지 설치 전제 조건: 물리 머신 BIOS에서 가상화 지원 VT-x를 활성화합니다(대부분의 컴퓨터에서 기본 활성화됨). VMware 내부에 중첩 설치하는 경우 VMware 설정에서도 VT-x 지원을 활성화해야 합니다.\n권장 리소스 구성: CPU 4코어, 메모리 4GB.\n1 2 [root@cloud ~]# yum -y groupinstall \u0026#34;Virtualization*\u0026#34; [root@cloud ~]# systemctl restart libvirtd.service 2. 그래픽 도구로 가상 머신 설치 이 실습에서는 그래픽 도구 virt-manager를 사용하여 가상 머신을 생성하고 설치합니다.\n참고: 그래픽 마법사의 조작 과정은 이 글에서 스크린샷으로 보여드리지 않습니다. 본문은 명령줄 조작을 위주로 하며, 그래픽 인터페이스는 안내에 따라 클릭하면 완료할 수 있습니다. 또한, \u0026ldquo;호스트 머신\u0026quot;이란 KVM 가상 머신을 실행하는 머신을 의미합니다. 이 실습은 VMware에서 중첩 가상화를 수행하므로 여기서 호스트 머신은 물리 머신이 아닌 VMware 가상 머신입니다.\n먼저 시스템 설치 이미지를 준비합니다: /isos 디렉터리를 생성하고 CentOS 8.4 ISO 파일을 복사합니다.\n1 [root@cloud ~]# mkdir /isos 그래픽 관리 인터페이스를 실행합니다:\n1 [root@cloud ~]# virt-manager 인터페이스에서 새 가상 머신 생성을 클릭하고, /isos에 넣은 ISO를 설치 소스로 선택한 후 마법사에 따라 CPU, 메모리, 디스크를 구성하면 설치가 완료됩니다. 설치된 가상 머신의 디스크 파일은 기본적으로 /var/lib/libvirt/images/ 디렉터리에 server1.qcow2와 같은 이름으로 저장됩니다:\n1 2 3 [root@cloud ~]# cd /var/lib/libvirt/images/ [root@cloud images]# ls server1.qcow2 3. 디스크 생성 이전 단계의 그래픽 설치 시 virt-manager가 자동으로 디스크를 생성합니다. 여기서는 수동 생성 방법을 소개하며, 이는 나중에 클라우드 이미지 제작 및 가상 머신에 디스크 추가 시 사용됩니다. qemu-img를 사용하여 qcow2 형식 디스크를 생성합니다:\n1 [root@cloud ~]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server1.qcow2 10g 팁: 이 방식으로 클라우드 이미지를 생성할 때 용량이 작을수록 생성 속도가 빠릅니다. 이미지를 최소한으로 만드는 방법을 연구해 볼 수 있습니다. 일반적으로 시스템 디스크는 성능 요구사항이 높지 않으며, 실제 성능 병목은 데이터 디스크에 있습니다.\n4. 브리지 모드 생성 (업링크가 있는 가상 스위치) 네트워크 구성 디렉터리로 이동하여 기존 네트워크 인터페이스를 기반으로 브리지 구성을 복사합니다:\n1 2 [root@cloud ~]# cd /etc/sysconfig/network-scripts/ [root@cloud network-scripts]# cp ifcfg-ens33 ifcfg-br0 브리지 인터페이스 ifcfg-br0 편집:\n1 [root@cloud network-scripts]# cat ifcfg-br0 1 2 3 4 5 6 7 8 TYPE=Bridge NAME=br0 DEVICE=br0 ONBOOT=yes IPADDR=192.168.8.99 PREFIX=24 GATEWAY=192.168.8.254 DNS1=192.168.8.254 물리 인터페이스 ifcfg-ens33을 br0에 브리지하도록 편집:\n1 [root@cloud network-scripts]# cat ifcfg-ens33 1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 네트워크 구성을 다시 로드하고 연결을 재시작합니다:\n1 2 3 [root@cloud ~]# nmcli connection reload [root@cloud ~]# nmcli connection down ens33 ; nmcli connection up ens33 [root@cloud ~]# nmcli connection down br0 ; nmcli connection up br0 5. 클라우드 이미지 제작 요구사항: 사용자 정의 클라우드 머신 이미지를 생성합니다.\nKVM 가상 머신 배포 완료 후, 가상 머신에 접속하여 필요한 소프트웨어를 설치합니다:\n로컬 yum 저장소 구성 필수 패키지 설치: 1 # yum -y install net-tools vim-enhanced bash-completion cloud-init /etc/cloud/cloud.cfg 구성 파일 편집, 실제 요구에 따라 매개변수 조정 /etc/ssh/ssh_host* 삭제 init 0으로 종료 마지막으로 이미지를 압축합니다:\n1 [root@cloud images]# qemu-img convert -c -O qcow2 /var/lib/libvirt/images/server1.qcow2 /isos/server1.qcow2 IV. KVM 관리 virsh 대화형 인터페이스 진입:\n1 [root@cloud ~]# virsh 주요 가상 머신 관리 명령은 다음과 같습니다.\n모든 가상 머신 나열:\n1 2 3 4 [root@cloud ~]# virsh list --all Id Name State ------------------------- - server1 shut off 가상 머신 시작 / 종료 / 강제 종료 / 재부팅:\n1 2 3 4 [root@cloud ~]# virsh start server1 [root@cloud ~]# virsh shutdown server1 [root@cloud ~]# virsh destroy server1 [root@cloud ~]# virsh reboot server1 6. 가상 머신 콘솔 인터페이스 진입 KVM 가상 머신에 진입하여 가상 머신에 가상 콘솔을 추가함으로써 호스트 머신에서 콘솔 방식으로 가상 머신에 접근할 수 있습니다.\n가상 머신 내에서 grub 구성 편집:\n1 [root@VM1 ~]# vim /etc/default/grub GRUB_CMDLINE_LINUX 끝에 console=ttyS0 추가:\n1 GRUB_CMDLINE_LINUX=\u0026#34;crashkernel=auto resume=UUID=94e88e69-fad6-4d1f-99cf-0ba83cc793b5 rhgb quiet console=ttyS0\u0026#34; grub 구성 재생성 및 재부팅:\n1 2 [root@VM1 ~]# grub2-mkconfig -o /boot/grub2/grub.cfg [root@VM1 ~]# reboot 호스트 머신에서 콘솔 연결:\n1 [root@cloud ~]# virsh console server1 콘솔 종료 단축키는 Ctrl + ]입니다.\n7. 구성을 통한 가상 머신 디스크 추가 (1) 디스크 파일 생성:\n1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/disk2.qcow2 5g (2) 구성 파일로 디스크 추가:\n1 [root@cloud ~]# vim /etc/libvirt/qemu/server1.xml 구성에 다음 disk 섹션 추가:\n1 2 3 4 5 6 \u0026lt;disk type=\u0026#39;file\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;qcow2\u0026#39;/\u0026gt; \u0026lt;source file=\u0026#39;/var/lib/libvirt/images/disk2.qcow2\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vdb\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x07\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; 서비스 재시작 및 가상 머신 시작:\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server1 [root@cloud ~]# virsh console server1 KVM 가상 머신 내에서 디스크 확인:\n1 2 3 [root@localhost ~]# fdisk -l /dev/vda /dev/vdb 비교: Xen 가상화에서 디스크 장치명은 /dev/xvda, /dev/xvdb입니다.\n8. KVM 가상 머신 빠른 생성 기존 가상 머신의 XML 구성을 복사하여 빠르게 생성:\n1 2 3 4 [root@cloud qemu]# cp server1.xml server2.xml [root@cloud qemu]# uuidgen f3ccabc3-ca4c-41f2-8d6f-e99077c24eeb [root@cloud qemu]# vim server2.xml name, uuid, 디스크 경로 및 MAC 주소 등 주요 매개변수 수정:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 \u0026lt;domain type=\u0026#39;kvm\u0026#39;\u0026gt; \u0026lt;name\u0026gt;server2\u0026lt;/name\u0026gt; \u0026lt;uuid\u0026gt;f3ccabc3-ca4c-41f2-8d6f-e99077c24eeb\u0026lt;/uuid\u0026gt; \u0026lt;metadata\u0026gt; \u0026lt;disk type=\u0026#39;file\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;qcow2\u0026#39;/\u0026gt; \u0026lt;source file=\u0026#39;/isos/server1.qcow2\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vda\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x04\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; \u0026lt;interface type=\u0026#39;bridge\u0026#39;\u0026gt; \u0026lt;mac address=\u0026#39;52:54:00:96:f6:a2\u0026#39;/\u0026gt; \u0026lt;source bridge=\u0026#39;br0\u0026#39;/\u0026gt; \u0026lt;model type=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x01\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/interface\u0026gt; 서비스 재시작 및 새 가상 머신 시작:\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server2 [root@cloud ~]# virsh list --all V. KVM 네트워크 구성 KVM에는 일반적으로 세 가지 네트워크 모드가 있습니다: NAT 모드, 호스트 전용 모드(Host-Only), 브리지 모드.\n1. NAT 모드 iptables 주소 변환(MASQUERADE)을 통해 NAT를 구현합니다:\n1 # iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE 또는 SNAT으로 소스 주소를 지정:\n1 # iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j SNAT --to-source 192.168.8.99 NAT 규칙 확인:\n1 [root@cloud ~]# iptables -t nat -nL 기본 네트워크 구성 파일은 /etc/libvirt/qemu/networks에 위치:\n1 2 3 [root@cloud networks]# pwd /etc/libvirt/qemu/networks [root@cloud networks]# vim default.xml 2. 호스트 전용 모드 호스트 전용 모드에서는 가상 머신이 호스트 머신과만 통신할 수 있으며 외부 네트워크에 접근할 수 없습니다.\n3. KVM 가상 머신에서 인터넷 접근 허용 (1) VMware 호스트 머신에 인터넷 접근이 가능한 네트워크 인터페이스를 추가합니다.\n(2) 호스트 머신 운영체제에 진입하여 이중 브리지를 구성합니다.\nifcfg-br0(내부망 브리지):\n1 2 3 4 5 6 TYPE=Bridge NAME=br0 DEVICE=br0 ONBOOT=yes IPADDR=192.168.8.99 PREFIX=24 ifcfg-br1(외부망 브리지, DHCP):\n1 2 3 4 5 TYPE=Bridge NAME=br1 DEVICE=br1 ONBOOT=yes BOOTPROTO=dhcp ifcfg-ens33을 br0에 브리지:\n1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 ifcfg-ens37을 br1에 브리지:\n1 2 3 4 5 TYPE=Ethernet NAME=ens37 DEVICE=ens37 ONBOOT=yes BRIDGE=br1 서비스 재시작 및 가상 머신 종료:\n1 2 3 4 5 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh shutdown server1 Domain server1 is being shutdown [root@cloud ~]# virsh shutdown server2 Domain server2 is being shutdown KVM 내 server1 가상 머신의 네트워크 인터페이스를 br1에 브리지하도록 수정하고, VM1 내에서 IP 획득 방식을 DHCP로 변경:\n1 2 3 4 5 6 7 8 9 10 11 [root@VM1 ~]# nmcli connection modify enp1s0 ipv4.method auto autoconnect yes [root@VM1 ~]# nmcli connection down enp1s0 ; nmcli connection up enp1s0 [root@VM1 ~]# ifconfig enp1s0: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1500 inet 192.168.31.158 netmask 255.255.255.0 broadcast 192.168.31.255 inet6 fe80::5054:ff:fe96:f6a1 prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether 52:54:00:96:f6:a1 txqueuelen 1000 (Ethernet) RX packets 1253 bytes 279527 (272.9 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 119 bytes 17932 (17.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 외부 네트워크 연결 테스트:\n1 2 3 4 5 [root@VM1 ~]# ping www.baidu.com PING www.a.shifen.com (183.2.172.17) 56(84) bytes of data. 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=1 ttl=52 time=23.6 ms 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=2 ttl=52 time=24.3 ms 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=3 ttl=52 time=23.5 ms VI. 명령줄로 KVM 가상 머신 설치 kickstart 자동 설치 스크립트를 활용하여 무인 방식으로 가상 머신을 일괄 설치할 수 있습니다.\n1. 디스크 생성 1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server3.qcow2 10g 2. 자동 설치 virt-install로 설치 소스와 kickstart 파일을 지정:\n1 2 3 4 5 [root@cloud ~]# virt-install -n server3 --memory 2048 --vcpu 2 \\ --disk /var/lib/libvirt/images/server3.qcow2 \\ --network bridge=br1 \\ -l http://192.168.31.96/pub \\ -x inst.ks=http://192.168.31.96/ks/ks01.cfg --vnc kickstart 구성 파일 ks01.cfg 예시:\n1 # vim /var/www/html/ks/ks01.cfg 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #version=RHEL8 # Use text install for fully unattended text repo --name=\u0026#34;AppStream\u0026#34; --baseurl=http://192.168.0.107/pub/AppStream repo --name=\u0026#34;BaseOS\u0026#34; --baseurl=http://192.168.0.107/pub/BaseOS reboot %packages @^minimal-environment kexec-tools %end # Keyboard layouts keyboard --xlayouts=\u0026#39;us\u0026#39; # System language lang en_US.UTF-8 # Network information network --bootproto=dhcp --activate --hostname=server3 --onboot=yes # Use network installation media url --url=http://192.168.0.107/pub/BaseOS # Run the Setup Agent on first boot firstboot --disable ignoredisk --only-use=vda clearpart --all --initlabel autopart # System timezone timezone Asia/Shanghai --utc # Root password rootpw --iscrypted $6$c31XqEglO8ECIli0$enF8T.LW3OknT.178Vb9m2HHYD6dqRdKF2QZia5.G5Tv/zMD//04S2h61oBOKihJZwPIUmWxLc7oXuWtkHQy10 user --name=admin --password=$6$Ey847S5bcqYg8ktk$sd4BJeFF01IFsGOaFwRoqXoYuZ5H1RV3bLC6n6gQGa1tNAjplFDhTnm3Cjw2pgwltIE.xr27X7E/ro4gF2JSG1 --iscrypted --gecos=\u0026#34;admin\u0026#34; %addon com_redhat_kdump --enable --reserve-mb=\u0026#39;auto\u0026#39; %end %anaconda pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty %end 참고: 위 설치 소스 주소(192.168.0.107), 호스트명(server3) 및 rootpw, user 뒤의 비밀번호 해시는 예시입니다. 실제 사용 시 자신의 환경 주소와 생성한 비밀번호 암호문으로 교체하십시오.\n","date":"2026-08-02","description":"","objectID":"/ko/posts/kvm-virtualization-guide/","readingTime":9,"tags":["KVM","가상화","Linux","CentOS"],"title":"KVM 가상화 입문부터 실전까지: 배포, 관리 및 네트워크 구성","url":"/ko/posts/kvm-virtualization-guide/","wordCount":1721},{"categories":["컨테이너 기술"],"content":"1. 적절한 베이스 이미지 선택 1 2 3 4 5 6 7 8 9 10 # ❌ 너무 큼 FROM ubuntu:22.04 # ~77MB FROM python:3.11 # ~1GB # ✅ 추천 FROM python:3.11-alpine # ~50MB FROM python:3.11-slim # ~130MB # ✅ 최소 크기 FROM gcr.io/distroless/python3 # ~20MB 베이스 이미지 크기 ubuntu:22.04 ~77MB python:3.11 ~1GB python:3.11-alpine ~50MB distroless ~20MB 2. 멀티스테이지 빌드 사용 가장 효과적인 최적화 방법: 컴파일 스테이지에서는 큰 이미지를 사용하고 실행 스테이지에서는 산출물만 복사합니다.\n1 2 3 4 5 6 7 8 9 10 11 # 스테이지 1: 컴파일 FROM golang:1.21 AS builder WORKDIR /app COPY . . RUN go build -o myapp # 스테이지 2: 실행 FROM alpine:3.18 WORKDIR /app COPY --from=builder /app/myapp . CMD [\u0026#34;./myapp\u0026#34;] Go 애플리케이션이 ~1GB에서 ~15MB로 축소됩니다.\n3. RUN 명령어 병합 1 2 3 4 5 6 7 8 9 10 11 12 # ❌ 여러 레이어 RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y wget RUN rm -rf /var/lib/apt/lists/* # ✅ 하나의 레이어로 병합 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends \\ curl \\ wget \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* 4. 빌드 캐시 정리 1 2 3 4 5 6 7 8 9 # Python - pip 캐시 비활성화 RUN pip install --no-cache-dir -r requirements.txt # Node.js - npm 캐시 정리 RUN npm ci --only=production \u0026amp;\u0026amp; \\ npm cache clean --force # Go - 디버그 정보 제거 RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 5. .dockerignore 사용 1 2 3 4 5 6 7 8 9 10 11 # .dockerignore .git node_modules *.md .env .vscode __pycache__ *.pyc dist build coverage 1 2 # 컨텍스트 크기 확인 docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; 6. 필요한 패키지만 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # ❌ 실행 이미지에 빌드 도구가 포함됨 FROM ubuntu:22.04 RUN apt-get install -y build-essential gcc make cmake COPY . . RUN make \u0026amp;\u0026amp; make install CMD [\u0026#34;./myapp\u0026#34;] # ✅ 빌드와 실행 분리 FROM ubuntu:22.04 AS builder RUN apt-get install -y build-essential gcc make COPY . . RUN make \u0026amp;\u0026amp; make install FROM ubuntu:22.04 COPY --from=builder /app/myapp . CMD [\u0026#34;./myapp\u0026#34;] 7. \u0026ndash;no-install-recommends 사용 1 2 3 4 5 # ❌ 모든 추천 패키지 설치 RUN apt-get install -y curl # ✅ 필수 패키지만 설치 (의존성 약 50% 감소) RUN apt-get install -y --no-install-recommends curl 8. 정적 리소스 압축 1 2 3 4 5 6 7 8 9 10 11 # JavaScript 압축 RUN uglifyjs app.js -o app.min.js # CSS 압축 RUN cssnano app.css app.min.css # 이미지 압축 RUN find . -name \u0026#34;*.png\u0026#34; -exec optipng {} \\; # Go 바이너리 디버그 정보 제거 RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 9. Squash로 레이어 병합 1 2 # 모든 레이어를 하나로 병합하여 중간 레이어의 중복 파일 제거 docker build --squash -t myapp:slim . /etc/docker/daemon.json에서 experimental 기능을 활성화해야 합니다.\n10. 이미지 레이어 분석 1 2 3 4 5 6 7 # dive 도구로 각 레이어의 크기와 내용 분석 docker run --rm -it \\ -v /var/run/docker.sock:/var/run/docker.sock \\ wagoodman/dive myapp:latest # 또는 docker history 사용 docker history myapp:latest 실전 사례: Python 애플리케이션 1 2 3 4 5 6 # ❌ 최적화 전 (1.2GB) FROM python:3.11 WORKDIR /app COPY . . RUN pip install -r requirements.txt CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 최적화 후 (150MB, 87% 감소) FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir --user -r requirements.txt FROM python:3.11-slim WORKDIR /app COPY --from=builder /root/.local /root/.local COPY . . ENV PATH=/root/.local/bin:$PATH CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 실전 사례: Node.js 애플리케이션 1 2 3 4 5 6 # ❌ 최적화 전 (1.1GB) FROM node:18 WORKDIR /app COPY . . RUN npm install CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 최적화 후 (180MB, 84% 감소) FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production FROM node:18-alpine WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY . . USER node CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 추천 도구 도구 용도 dive 이미지의 각 레이어 내용과 크기 분석 docker-slim 이미지 자동 최적화 hadolint Dockerfile 정적 검사 trivy 이미지 보안 스캔 ","date":"2026-07-30","description":"Docker 이미지 크기를 90% 이상 줄이는 검증된 10가지 팁을 소개합니다.","objectID":"/ko/posts/dockerfile-smaller-image/","readingTime":4,"tags":["Docker","Dockerfile","이미지 최적화","컨테이너화"],"title":"Dockerfile 이미지가 너무 크다고요? 이미지 크기를 90% 줄이는 10가지 팁","url":"/ko/posts/dockerfile-smaller-image/","wordCount":692},{"categories":["컨테이너 기술"],"content":"1. 명령어 순서 최적화 (캐시 활용) 변경 빈도가 낮은 명령어를 앞에, 높은 명령어를 뒤에 배치하세요. 특정 레이어의 캐시가 무효화되면 그 이후의 모든 레이���가 다시 빌드됩니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ 잘못된 예: 코드 수정 시마다 의존성을 다시 설치 FROM node:18-alpine WORKDIR /app COPY . . # 코드는 자주 변경됨 RUN npm ci --production # 의존성은 거의 변경되지 않음 CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # ✅ 올바른 예: 의존성 변경 시에만 다시 설치 FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # 의존성 정의 (거의 변경되지 않음) RUN npm ci --production # 의존성 설치 (거의 변경되지 않음) COPY . . # 코드 (자주 변경됨) CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 # 캐시 적중 여부 확인 docker build -t myapp . 2\u0026gt;\u0026amp;1 | grep -E \u0026#34;(CACHED|Using cache)\u0026#34; 2. .dockerignore로 컨텍스트 축소 docker build는 .git, node_modules 등 불필요한 파일을 포함한 전체 디렉터리를 Docker daemon으로 전송합니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # .dockerignore .git .github .vscode .idea node_modules dist build *.md *.log .env .env.local .DS_Store coverage .nyc_output 1 2 3 4 # 컨텍스트 크기 확인 docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; # 최적화 전: Sending build context to Docker daemon 1.2GB # 최적화 후: Sending build context to Docker daemon 50MB 3. 경량 베이스 이미지 사용 베이스 이미지 크기 설명 ubuntu:22.04 ~77MB 완전한 Ubuntu python:3.11 ~1GB 완전한 Python 포함 python:3.11-slim ~130MB 경량 버전 python:3.11-alpine ~50MB Alpine 버전 1 2 3 4 5 6 7 8 # ❌ 너무 큼 FROM python:3.11 # 1GB # ✅ 추천 FROM python:3.11-slim # 130MB # ✅ 최소 크기 (musl 호환성 문제 주의) FROM python:3.11-alpine # 50MB Alpine은 glibc 대신 musl을 사용하므로 일부 소프트웨어가 호환되지 않을 수 있습니다. 문제가 발생하면 slim 버전을 선택하세요.\n4. RUN 명령어 병합으로 레이어 수 감소 각 RUN은 새로운 레이어를 생성하며, 레이어가 많을수록 빌드가 느려지고 이미지가 커집니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ 5개 레이어 RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y wget RUN apt-get clean RUN rm -rf /var/lib/apt/lists/* # ✅ 1개 레이어 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends \\ curl \\ wget \u0026amp;\u0026amp; \\ apt-get clean \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* 1 2 # 이미지 레이어 수 확인 docker history myapp:latest 5. 멀티스테이지 빌드 사용 한 스테이지에서 빌드하고 다른 스테이지에서 실행하여 필요한 파일만 복사합니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 스테이지 1: 빌드 FROM golang:1.21-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o app . # 스테이지 2: 실행 (~15MB) FROM alpine:3.18 RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/app . EXPOSE 8080 CMD [\u0026#34;./app\u0026#34;] 방식 이미지 크기 빌드 시간 싱글 스테이지 ~1.2GB 5분 멀티 스테이지 ~15MB 2분 6. BuildKit으로 빌드 가속화 BuildKit은 병렬 빌드와 고급 캐싱을 지원합니다.\n1 2 3 4 5 6 7 # BuildKit 활성화 export DOCKER_BUILDKIT=1 # 또는 /etc/docker/daemon.json에 영구 설정 # { # \u0026#34;features\u0026#34;: { \u0026#34;buildkit\u0026#34;: true } # } 1 2 3 4 5 6 7 8 9 10 # 캐시 마운트 사용 (BuildKit 기능) # syntax=docker/dockerfile:1 FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # 캐시 마운트로 매번 의존성을 다시 다운로드하지 않음 RUN --mount=type=cache,target=/root/.npm \\ npm ci --production COPY . . CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 # BuildKit 활성화 여부 확인 docker build --progress=plain -t myapp . 2\u0026gt;\u0026amp;1 | head -5 7. 캐시 가져오기/내보내기 사용 CI/CD 환경에서는 매 빌드 후 캐시가 손실됩니다. 캐시 가져오기/내보내기를 사용하면 빌드 간 캐시를 유지할 수 있습니다.\n1 2 3 4 5 6 7 8 9 10 11 # 로컬로 내보내기 docker build \\ --cache-from type=local,src=/tmp/cache \\ --cache-to type=local,dest=/tmp/cache \\ -t myapp . # 레지스트리를 캐시로 사용 docker build \\ --cache-from type=registry,ref=myregistry/myapp:cache \\ --cache-to type=registry,ref=myregistry/myapp:cache,mode=max \\ -t myapp . 1 2 3 4 5 6 7 8 9 # GitHub Actions 예제 - name: Build and push uses: docker/build-push-action@v5 with: context: . push: true tags: myapp:latest cache-from: type=gha cache-to: type=gha,mode=max 8. 의존성 병렬 설치 1 2 3 4 5 6 7 8 9 10 11 12 # Node.js - npm ci가 npm install보다 빠름 RUN npm ci --production # Python - 캐시 비활성화 RUN pip install --no-cache-dir -r requirements.txt # Go - 모듈 병렬 다운로드 RUN go mod download # Rust - cargo-chef 사용 COPY --from=chef /app/recipe.json recipe.json RUN cargo chef cook --release --recipe-path recipe.json 9. 빌드 캐시 및 임시 파일 정리 동일한 RUN 명령어 내에서 정리하여 파일이 레이어에 남지 않도록 합니다.\n1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 올바른 예: 동일 레이어에서 정리 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends build-essential \u0026amp;\u0026amp; \\ make \u0026amp;\u0026amp; make install \u0026amp;\u0026amp; \\ apt-get purge -y --auto-remove build-essential \u0026amp;\u0026amp; \\ apt-get clean \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # ❌ 잘못된 예: 정리가 다른 레이어에서 수행되어 파일이 여전히 존재함 RUN apt-get update \u0026amp;\u0026amp; apt-get install -y build-essential RUN make \u0026amp;\u0026amp; make install RUN apt-get clean # 너무 늦었습니다! 이��� 레이어에 파일이 이미 포함됨 언어별 정리 명령어:\n1 2 3 4 5 6 7 8 # Node.js RUN npm ci --production \u0026amp;\u0026amp; npm cache clean --force # Python RUN pip install --no-cache-dir -r requirements.txt # Go RUN go build -o app . \u0026amp;\u0026amp; go clean -cache 10. 특정 버전 태그 사용 1 2 3 4 5 6 7 8 # ❌ latest는 변경될 수 있어 캐시를 예측할 수 없음 FROM node:latest # ✅ 특정 버전 FROM node:18.17.0-alpine3.18 # ✅ 더 안전: SHA256 다이제스트 사용 FROM node:18.17.0-alpine3.18@sha256:abc123... 이미지 레이어 상세 설명 1 2 # 이미지 레이어 히스토리 확인 docker history \u0026lt;image-name\u0026gt; 새 레이어를 생성하는 명령어: FROM, RUN, COPY, ADD\n레이어를 생성하지 않는 명령어 (메타데이터만): CMD, ENTRYPOINT, ENV, EXPOSE, WORKDIR, USER, LABEL, ARG, VOLUME\n1 2 3 4 5 6 7 8 9 10 11 # 4개 레이어 생성 FROM node:18-alpine # 레이어 1 RUN apk add --no-cache curl # 레이어 2 COPY package.json . # 레이어 3 RUN npm install # 레이어 4 # 이 명령어들은 레이어를 추가하지 않음 WORKDIR /app # 레이어 없음 ENV NODE_ENV=production # 레이어 없음 EXPOSE 3000 # 레이어 없음 CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # 레이어 없음 최적화 효과 비교 1 2 3 4 5 6 7 8 9 # ❌ 최적화 전 (8분, 1.2GB) FROM ubuntu:22.04 RUN apt-get update RUN apt-get install -y python3 python3-pip RUN apt-get install -y build-essential WORKDIR /app COPY . . RUN pip3 install -r requirements.txt CMD [\u0026#34;python3\u0026#34;, \u0026#34;app.py\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ✅ 최적화 후 (1.5분, 150MB) # syntax=docker/dockerfile:1 FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN --mount=type=cache,target=/root/.cache/pip \\ pip install --no-cache-dir -r requirements.txt FROM python:3.11-slim WORKDIR /app COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY . . EXPOSE 8000 CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 지표 최적화 전 최적화 후 개선 빌드 시간 8분 1.5분 81% ⬇️ 이미지 크기 1.2GB 150MB 87% ⬇️ 컨텍스트 크기 500MB 50MB 90% ⬇️ 캐시 적중률 10% 90% 80% ⬆️ 모니터링 및 디버깅 1 2 3 4 5 6 7 8 9 10 11 # 상세 빌드 시간 표시 time docker build -t myapp . # BuildKit 상세 진행 상황 DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp . # 이미지 레이어 분석 docker history myapp:latest # dive 도구로 심층 분석 dive myapp:latest ","date":"2026-07-30","description":"캐시 전략부터 멀티스테이지 빌드까지, Docker 빌드 시간을 대폭 단축하는 검증된 10가지 팁을 소개합니다.","objectID":"/ko/posts/dockerfile-faster-build/","readingTime":6,"tags":["Docker","Dockerfile","성능 최적화","CI/CD"],"title":"Dockerfile 빌드 속도를 높이는 방법? 10가지 실용적인 최적화 팁","url":"/ko/posts/dockerfile-faster-build/","wordCount":1213},{"categories":["컨테이너 기술"],"content":"1. 기본 문법 두 명령어의 문법은 거의 동일하지만 동작 방식에 큰 차이가 있습니다:\n1 2 3 4 5 # COPY - 순수한 파일 복사, 추가 처리를 하지 않음 COPY [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; # ADD - 자동 압축 해제 및 원격 URL을 지원하지만 예측 불가능한 동작 가능 ADD [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; 2. 압축 파일 자동 해제 ADD는 로컬 압축 파일을 자동으로 해제하지만 COPY는 그렇지 않습니다:\n1 2 3 4 5 6 7 # ADD는 .tar.gz를 자동 해제 ADD app.tar.gz /opt/app/ # 결과: /opt/app/ 아래에 압축이 해제된 내용 # COPY는 파일 자체만 복사 COPY app.tar.gz /opt/app/ # 결과: /opt/app/app.tar.gz는 원본 압축 파일 지원하는 압축 형식: .tar, .tar.gz/.tgz, .tar.bz2, .tar.xz\n압축 파일을 그대로 복사하고 해제하지 않으려면 반드시 COPY를 사용해야 합니다.\n3. 원격 URL 다운로드 ADD는 URL에서 직접 파일을 다운로드할 수 있지만 COPY는 지원하지 않습니다:\n1 2 3 4 5 # ADD는 원격 파일 다운로드 가능 (하지만 권장하지 않음) ADD https://example.com/app.tar.gz /opt/ # COPY는 URL을 지원하지 않음 COPY https://example.com/app.tar.gz /opt/ # 오류 발생! ADD로 원격 파일을 다운로드하는 대신 RUN + curl을 사용하는 것을 추천합니다. ADD 다운로드는 빌드 캐시를 활용할 수 없고, 오류 처리가 불가능하며, 무결성 검증도 할 수 없기 때문입니다.\n1 2 3 4 # 추천: RUN + curl RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \\ \u0026amp;\u0026amp; tar -xzf /tmp/app.tar.gz -C /opt/ \\ \u0026amp;\u0026amp; rm /tmp/app.tar.gz 4. 디렉터리 자동 생성 두 명령어 모두 존재하지 않는 중간 디렉터리를 자동으로 생성합니다:\n1 2 3 4 5 6 # /etc/myapp/config/가 존재하지 않으면 자동 생성 COPY app.conf /etc/myapp/config/ ADD app.conf /etc/myapp/config/ # 수동으로 mkdir 불필요 # RUN mkdir -p /etc/myapp/config ← 불필요 5. 빌드 캐시 동작 1 2 3 4 5 # COPY - 파일 내용을 엄격하게 비교, 내용이 변경되어야 캐시 무효화 COPY config/app.conf /etc/app/ # ADD - 압축 파일 내용이 동일하면 캐시 유지 ADD app.tar.gz /opt/app/ 상황 COPY ADD 파일 내용 변경 캐��� 무효화 캐시 무효화 파일 메타데이터 변경 캐시 무효화 캐시 무효화 압축 파일 내용 동일 N/A 캐시 유지 6. 기능 비교 기능 COPY ADD 로컬 파일 복사 ✅ ✅ 디렉터리 복사 ✅ ✅ 와일드카드 지원 ✅ ✅ 디렉터리 자동 생성 ✅ ✅ 압축 파일 자동 해제 ❌ ✅ 원격 URL 지원 ❌ ✅ 의미 명확성 ✅ 높음 ❌ 낮음 사용 권장 ✅ 우선 사용 ⚠️ 특정 상황 7. 사용 사례 COPY에 적합한 경우:\n1 2 3 4 5 6 7 8 9 # 설정 파일 복사 COPY nginx.conf /etc/nginx/nginx.conf # 애플리케이션 코드 복사 COPY src/ /app/src/ COPY package.json /app/ # 멀티스테이지 빌드에서 바이너리 복사 COPY --from=builder /app/build/app /usr/local/bin/ ADD는 자동 압축 해제가 필요한 경우에만 사용:\n1 2 3 4 5 # ADD의 적절한 사용 ADD node-v18.17.0-linux-x64.tar.gz /usr/local/ # 일�� 파일에는 ADD를 사용하지 마세요 ADD app.conf /etc/app/ # ← COPY로 대체 8. 모범 사례 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # 규칙 1: 기본적으로 COPY 사용 COPY requirements.txt /app/ # ✅ ADD requirements.txt /app/ # ❌ 불필요 # 규칙 2: 압축 해제가 필요할 때만 ADD 사용 ADD node-v18.tar.gz /usr/local/ # ✅ 압축 해제 필요 # 규칙 3: 원격 파일은 RUN + curl 사용 RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \\ \u0026amp;\u0026amp; tar -xzf /tmp/app.tar.gz -C /opt/ \\ \u0026amp;\u0026amp; rm /tmp/app.tar.gz # ✅ ADD https://example.com/app.tar.gz /opt/ # ❌ # 규칙 4: .dockerignore로 컨텍스트 축소 # .dockerignore # .git # node_modules # *.md # .env # 규칙 5: COPY 순서를 적절히 배치하여 캐시 활용 COPY package.json package-lock.json ./ # 의존성 먼저 복사 (변경 적음) RUN npm ci --production COPY . . # 코드 나중에 복사 (변경 많음) 9. 흔한 함정 1 2 3 4 5 6 7 8 9 10 # 함정 1: 의도치 않은 자동 압축 해제 ADD app.tar.gz /opt/app/ # 파일을 복사했다고 생각했지만 실제로는 디렉터리로 해제됨 # 함정 2: 원격 파일 캐시 불가 ADD https://github.com/user/repo/releases/download/v1.0/app.tar.gz /tmp/ # 매 빌드마다 다시 다운로드 # 함정 3: 권한 문제 COPY --chown=app:app src/ /app/src/ # 소유자 지정 10. 선택 결정 1 2 3 4 5 6 7 파일 복사가 필요한가? ├── 로컬 압축 파일이며 해제가 필요한가? │ └── ADD 사용 ├── 로컬 일반 파일인가? │ └── COPY 사용 └── 원격 파일인가? └── RUN + curl 사용 ","date":"2026-07-30","description":"COPY와 ADD 모두 파일을 복사할 수 있지만 동작 방식에 큰 차이가 있습니다. 이 글에서는 비교와 예제를 통해 두 명령어의 차이점을 명확하게 설명합니다.","objectID":"/ko/posts/dockerfile-copy-vs-add/","readingTime":4,"tags":["Docker","Dockerfile","컨테이너화"],"title":"Dockerfile에서 COPY와 ADD의 차이점 완벽 정리","url":"/ko/posts/dockerfile-copy-vs-add/","wordCount":674},{"categories":["기술 튜토리얼"],"content":"1. 환경 요구사항 운영체제: CentOS 7/8/Stream 9 Node.js: v18 이상 메모리: 2GB 이상 권장 네트워크: Anthropic API 접근 가능해야 함 2. Node.js 설치 1 2 3 4 5 6 7 8 9 # NodeSource 저장소 설치 curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash - # Node.js 설치 sudo yum install -y nodejs # 확인 node -v npm -v Node.js 버전이 너무 낮으면 nvm으로 버전을 관리하세요:\n1 2 3 4 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash source ~/.bashrc nvm install 20 nvm use 20 3. Claude Code 설치 1 2 3 4 5 # 전역 설치 npm install -g @anthropic-ai/claude-code # 확인 claude --version 권한 문제가 발생하면 두 가지 해결 방법이 있습니다:\n1 2 3 4 5 6 7 8 # 방법 1: sudo sudo npm install -g @anthropic-ai/claude-code # 방법 2: npm 전역 디렉터리 변경 (추천) mkdir -p ~/.npm-global npm config set prefix \u0026#34;~/.npm-global\u0026#34; echo \u0026#39;export PATH=~/.npm-global/bin:$PATH\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc 4. API Key 설정 1 2 3 4 5 6 # 방법 1: 환경 변수 (추천) echo \u0026#39;export ANTHROPIC_API_KEY=\u0026#34;your-api-key-here\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc # 확인 echo $ANTHROPIC_API_KEY 방법 2: claude를 직접 실행하면 최초 실행 시 API Key 입력을 요청합니다.\n5. 기본 사용법 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 대화형 세션 시작 cd /path/to/your/project claude # 직접 질문하기 (비대화형 모드) claude -p \u0026#34;현재 디렉터리의 모든 파일 개수를 세는 Python 스크립트를 작성해줘\u0026#34; # 코드 분석 cat main.py | claude -p \u0026#34;이 코드의 잠재적 문제점을 분석해줘\u0026#34; # 설정 확인/수정 claude config # 최신 버전으로 업데이트 claude update 명령어 설명 claude 대화형 세션 시작 claude -p \u0026quot;질문\u0026quot; 직접 질문, 비대화형 모드 claude config 설정 확인/수정 claude update 최신 버전으로 업데이트 6. 네트워크 문제 해결 Anthropic API 연결이 타임아웃되는 경우:\n1 2 3 4 5 6 # 프록시 설정 export https_proxy=\u0026#34;http://your-proxy:port\u0026#34; export http_proxy=\u0026#34;http://your-proxy:port\u0026#34; # 연결 테스트 curl -s https://api.anthropic.com/ | head -5 7. 보안 권장사항 1 2 3 4 5 6 7 8 9 # API Key를 하드코딩하지 말고 항상 환경 변수를 사용하세요 echo \u0026#39;export ANTHROPIC_API_KEY=\u0026#34;sk-ant-xxx\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.bashrc # ✅ # ANTHROPIC_API_KEY=sk-ant-xxx 코드에 직접 작성 # ❌ # 파일 권한 제한 chmod 600 ~/.bashrc # 유출 여부 확인 grep -r \u0026#34;ANTHROPIC_API_KEY\u0026#34; --include=\u0026#34;*.py\u0026#34; --include=\u0026#34;*.js\u0026#34; . ","date":"2026-07-04","description":"CentOS에서 Claude Code를 처음부터 배포하는 방법으로, Node.js 설치, API Key 설정, 자주 발생하는 문제 해결 방법을 다룹니다.","objectID":"/ko/posts/centos-deploy-claude-code/","readingTime":2,"tags":["Claude Code","CentOS","AI","배포"],"title":"CentOS에서 Claude Code 배포 전체 가이드","url":"/ko/posts/centos-deploy-claude-code/","wordCount":358},{"categories":null,"content":"flannel 실전: Docker 크로스 호스트 컨테이너 네트워크 구축 1. 네트워크 환경 계획 etcd 호스트 IP: 192.168.8.88 호스트명: docker-network Host1 IP: 192.168.8.188 호스트명: web01 Host2 IP: 192.168.8.99 호스트명: web02 2. etcd 호스트 구성 단계 1: etcd 다운로드 및 압축 해제 github.com에서 etcd-v3.6.0-linux-amd64.tar.gz 소프트웨어 패키지를 다운로드합니다 소프트웨어 패키지를 etcd 호스트의 /root 디렉토리에 복사하고 압축을 해제합니다: 1 [root@docker-network ~]# tar -zxvf etcd-v3.6.0-linux-amd64.tar.gz 단계 2: etcd 실행 파일 설치 1 2 [root@docker-network ~]# cd etcd-v3.6.0-linux-amd64 [root@docker-network etcd-v3.6.0-linux-amd64]# cp etcd* /usr/local/bin/ 단계 3: 데이터 디렉토리 생성 1 [root@docker-network ~]# mkdir /var/lib/etcd 단계 4: etcd 서비스 구성 및 시작 etcd.service 파일을 생성합니다: 1 [root@docker-network ~]# vim /usr/lib/systemd/system/etcd.service 1 2 3 4 5 6 [Unit] Description=etcd_service [Service] ExecStart=/usr/local/bin/etcd --name etcd1 --data-dir /var/lib/etcd --listen-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379 --advertise-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379 [Install] WantedBy=multi-user.target 시스템 서비스 구성을 다시 로드하고 etcd를 시작합니다: 1 2 3 [root@docker-network ~]# systemctl daemon-reload [root@docker-network ~]# systemctl enable etcd.service [root@docker-network ~]# systemctl restart etcd.service etcd 리스닝 상태를 확인합니다: 1 2 3 [root@docker-network ~]# netstat -tulnp |grep :2379 tcp 0 0 192.168.8.88:2379 0.0.0.0:* LISTEN 1912/etcd tcp 0 0 127.0.0.1:2379 0.0.0.0:* LISTEN 1912/etcd 단계 5: etcd에 flannel 네트워크 구성 추가 1 etcdctl --endpoints http://127.0.0.1:2379 put /coreos.com/network/config \u0026#39;{\u0026#34;Network\u0026#34;: \u0026#34;172.16.0.0/16\u0026#34;, \u0026#34;SubnetLen\u0026#34;: 24, \u0026#34;SubnetMin\u0026#34;: \u0026#34;172.16.1.0\u0026#34;,\u0026#34;SubnetMax\u0026#34;: \u0026#34;172.16.10.0\u0026#34;, \u0026#34;Backend\u0026#34;: {\u0026#34;Type\u0026#34;: \u0026#34;vxlan\u0026#34;}}\u0026#39; 1 etcdctl --endpoints http://192.168.8.88:2379 get /coreos.com/network/config 구성 설명:\nNetwork: flannel 주소 풀 SubnetLen: 단일 호스트에 할당되는 docker0 서브넷 마스크 길이 (24비트는 255.255.255.0) SubnetMin/SubnetMax: 할당 가능한 서브넷 범위 (예: 172.16.1.0/24 ~ 172.16.10.0/24) Backend: 데이터 전달 모드 (vxlan은 호스트 간 통신 지원) 단계 6: 방화벽 비활성화 1 2 [root@docker-network ~]# systemctl disable firewalld [root@docker-network ~]# systemctl stop firewalld 3. web01 호스트 구성 (Host1: 192.168.8.188) 단계 1: flannel 다운로드 및 압축 해제 flannel-v0.26.7-linux-amd64.tar.gz를 호스트에 다운로드합니다 파일의 압축을 해제합니다: 1 [root@web01 ~]# tar -zxvf flannel-v0.26.7-linux-amd64.tar.gz 단계 2: flannel 실행 파일 설치 1 [root@web01 ~]# cp -p flanneld mk-docker-opts.sh /usr/local/bin/ 단계 3: flannel 서비스 구성 및 시작 flanneld.service 파일을 생성합니다: 1 [root@web01 ~]# vim /usr/lib/systemd/system/flanneld.service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Unit] Description=Flanneld Documentation=https://github.com/coreos/flannel After=network.target Before=docker.service [Service] User=root ExecStartPost=/usr/local/bin/mk-docker-opts.sh ExecStart=/usr/local/bin/flanneld --etcd-endpoints=http://192.168.8.88:2379 --iface=192.168.8.188 --ip-masq=true --etcd-prefix=/coreos.com/network Restart=on-failure Type=notify LimitNOFILE=65536 [Install] WantedBy=multi-user.target 매개변수 설명:\n--iface=192.168.8.188: etcd에 연결하는 로컬 IP를 지정합니다 --etcd-endpoints: etcd 서버 주소 서비스 구성을 다시 로드하고 flannel을 시작합니다:\n1 2 3 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl enable flanneld.service [root@web01 ~]# systemctl restart flanneld.service 단계 4: flannel 네트워크 인터페이스 확인 1 [root@web01 ~]# ifconfig flannel.1 인터페이스가 표시되어야 합니다 (예시 출력):\n1 2 3 4 5 6 7 8 flannel.1: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1450 inet 172.16.5.0 netmask 255.255.255.255 broadcast 0.0.0.0 inet6 fe80::f06c:96ff:fefe:b7ab prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether f2:6c:96:fe:b7:ab txqueuelen 0 (Ethernet) RX packets 315 bytes 26460 (25.8 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 314 bytes 34636 (33.8 KiB) TX errors 0 dropped 29 overruns 0 carrier 0 collisions 0 단계 5: docker 구성 파일 수정 flannel이 생성한 서브넷 환경 변수를 확인합니다: 1 [root@web01 ~]# cat /run/flannel/subnet.env 출력 예시:\n1 2 3 4 FLANNEL_NETWORK=172.16.0.0/16 FLANNEL_SUBNET=172.16.5.1/24 FLANNEL_MTU=1450 FLANNEL_IPMASQ=true 1 [root@web01 ~]# cat /run/docker_opts.env 출력 예시:\n1 2 3 4 DOCKER_OPT_BIP=\u0026#34;--bip=172.16.5.1/24\u0026#34; DOCKER_OPT_IPMASQ=\u0026#34;--ip-masq=false\u0026#34; DOCKER_OPT_MTU=\u0026#34;--mtu=1450\u0026#34; DOCKER_OPTS=\u0026#34; --bip=172.16.5.1/24 --ip-masq=false --mtu=1450\u0026#34; docker 서비스 구성을 수정합니다: 1 [root@web01 ~]# vim /usr/lib/systemd/system/docker.service [Service] 섹션에 다음을 추가합니다:\n1 2 3 ExecStart=/usr/bin/dockerd $DOCKER_OPTS -H fd:// --containerd=/run/containerd/containerd.sock ExecReload=/bin/kill -s HUP $MAINPID EnvironmentFile=-/run/docker_opts.env docker 서비스를 재시작합니다: 1 2 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl restart docker.service 단계 6: docker 네트워크 확인 nginx 컨테이너를 생성하고 접속합니다: 1 2 3 4 5 6 7 8 9 [root@web01 ~]# ifconfig docker0: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1450 inet 172.16.5.1 netmask 255.255.255.0 broadcast 172.16.5.255 inet6 fe80::42:f6ff:fe5d:5423 prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether 02:42:f6:5d:54:23 txqueuelen 0 (Ethernet) RX packets 10073 bytes 407167 (397.6 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 48185 bytes 38083646 (36.3 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 1 2 [root@web01 ~]# docker run -d -p 8800:80 nginx [root@web01 ~]# docker exec -it \u0026lt;컨테이너 ID\u0026gt; /bin/bash 컨테이너 IP를 확인합니다 (예시: 172.16.5.3): 1 2 root@컨테이너 ID:/# ifconfig eth0: inet 172.16.5.3 netmask 255.255.255.0 4. web02 호스트 구성 (Host2: 192.168.0.208) 단계 설명 구성 단계는 web01과 완전히 동일하며, 다음 매개변수만 수정하면 됩니다: --iface=192.168.8.99 (로컬 IP) docker 컨테이너 IP 예시: 172.16.5.2 (flannel에 의해 자동 할당) 5. 실험 확인 web01 컨테이너에서 web02 컨테이너 IP (예시: 172.16.9.2)로 ping을 수행합니다:\nweb02에서 컨테이너를 생성하고 컨테이너 IP 주소를 확인합니다.\n결과: 컨테이너 간 통신이 성공하고, flannel 네트워크 구성이 완료되었습니다.\n부록: etcd 네트워크 구성 조회 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/config /coreos.com/network/config {\u0026#34;Network\u0026#34;: \u0026#34;172.16.0.0/16\u0026#34;, \u0026#34;SubnetLen\u0026#34;: 24, \u0026#34;SubnetMin\u0026#34;: \u0026#34;172.16.1.0\u0026#34;,\u0026#34;SubnetMax\u0026#34;: \u0026#34;172.16.10.0\u0026#34;, \u0026#34;Backend\u0026#34;: {\u0026#34;Type\u0026#34;: \u0026#34;vxlan\u0026#34;}} 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/subnets --prefix --keys-only /coreos.com/network/subnets/172.16.5.0-24 /coreos.com/network/subnets/172.16.9.0-24 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/subnets/172.16.5.0-24 /coreos.com/network/subnets/172.16.5.0-24 {\u0026#34;PublicIP\u0026#34;:\u0026#34;192.168.8.188\u0026#34;,\u0026#34;PublicIPv6\u0026#34;:null,\u0026#34;BackendType\u0026#34;:\u0026#34;vxlan\u0026#34;,\u0026#34;BackendData\u0026#34;:{\u0026#34;VNI\u0026#34;:1,\u0026#34;VtepMAC\u0026#34;:\u0026#34;5a:67:18:61:6a:5d\u0026#34;}} ","date":"2025-05-21","description":"","objectID":"/ko/posts/flannel-network-lab/","readingTime":4,"tags":["flannel","Docker","네트워크","etcd"],"title":"flannel 실전: Docker 크로스 호스트 컨테이너 네트워크 구축","url":"/ko/posts/flannel-network-lab/","wordCount":751},{"content":"Harbor 액티브-스탠바이 배포 환경 준비 운영체제: CentOS 8.4 64bit\nHarbor 버전: harbor-offline-installer-v2.11.1.tgz\nDocker Compose: docker-compose-linux-x86_64\n역할 호스트명 IP 주소 주 Harbor Harbor01 192.168.8.161 스탠바이 Harbor Harbor02 192.168.8.162 Docker 호스트 web01 192.168.8.188 Harbor는 VMware의 오픈소스 엔터프라이즈 Docker 레지스트리 프로젝트로, 공식 Docker Registry를 기반으로 관리 UI, 역할 기반 접근 제어(RBAC), AD/LDAP 통합, 감사 로그 등의 기업 기능을 추가했습니다. Harbor의 각 컴포넌트는 Docker 컨테이너로 구축되며, Docker Compose를 사용하여 배포됩니다.\n파트 1: 주 Harbor 배포 (HTTP 모드, 인증서 없음) 이 방법은 내부 네트워크에 적합하며, SSL 인증서 없이 HTTP로 직접 Harbor에 접근합니다.\n1. Docker 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 # 기존 yum 저장소 파일 삭제 rm -rf /etc/yum.repos.d/* # Alibaba Cloud 미러에서 yum 저장소 파일 다운로드 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo # Docker CE 설치 yum install -y yum-utils yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # Docker 시작 및 부팅 시 자동 시작 설정 systemctl enable docker.service --now 2. Docker 미러 설정 1 vim /etc/docker/daemon.json 다음 내용 입력:\n1 2 3 4 5 6 { \u0026#34;registry-mirrors\u0026#34;: [ \u0026#34;https://37564ea7e7cc4824a5049fd666807c27.mirror.swr.myhuaweicloud.com\u0026#34; ], \u0026#34;insecure-registries\u0026#34;: [\u0026#34;http://192.168.8.161\u0026#34;] } 1 2 systemctl daemon-reload systemctl restart docker.service 3. Docker Compose 및 Harbor 설치 1 2 3 4 5 6 7 8 9 10 # docker-compose 및 harbor 오프라인 패키지를 /data에 배치 ls /data # docker-compose-linux-x86_64 harbor-offline-installer-v2.11.1.tgz # docker-compose 설치 cp /data/docker-compose-linux-x86_64 /usr/local/bin/docker-compose chmod a+x /usr/local/bin/docker-compose # harbor 압축 해제 tar -zxvf /data/harbor-offline-installer-v2.11.1.tgz -C /usr/local/ 4. Harbor 설정 수정 1 2 3 cd /usr/local/harbor/ cp harbor.yml.tmpl harbor.yml vim harbor.yml 다음 핵심 설정 수정:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 hostname: 192.168.8.161 http: port: 80 # https 섹션 전체 주석 처리 (HTTP 모드에서는 인증서 불필요) #https: # port: 443 # certificate: /your/certificate/path # private_key: /your/private/key/path harbor_admin_password: Harbor12345 data_volume: /images 나머지 설정은 기본값으로 유지합니다.\n5. 설치 실행 1 ./install.sh 설치 후 브라우저에서 http://192.168.8.161에 접속하여 admin / Harbor12345로 로그인합니다.\n6. 사용자 및 프로젝트 생성 Harbor Web UI에서 user1 생성:\nuser1로 로그인:\nuser1로 새 프로젝트(예: openssh) 생성:\n7. 이미지 푸시 및 풀 테스트 Docker 호스트(web01)에서 작업:\n1 2 3 4 5 6 7 8 9 10 11 # Harbor 로그인 docker login 192.168.8.161 Username: user1 Password: # Login Succeeded # 이미지 태깅 docker tag nginx:latest 192.168.8.161/openssh/nginx:latest # Harbor에 푸시 docker push 192.168.8.161/openssh/nginx:latest 성공적으로 푸시된 후 Harbor에서 이미지 확인:\n1 2 3 # 로컬 이미지 삭제 후 다시 풀 docker image rm -f 192.168.8.161/openssh/nginx:latest docker pull 192.168.8.161/openssh/nginx:latest 풀 후 Harbor에 다운로드 횟수 +1 표시:\n파트 2: 스탠바이 Harbor 배포 스탠바이 배포는 주 Harbor와 동일하며, hostname만 스탠바이 IP로 변경합니다.\n1 vim /usr/local/harbor/harbor.yml 1 hostname: 192.168.8.162 배포 후 http://192.168.8.162에 접속하여 user2 생성:\n스탠바이 Harbor에 user2로 로그인:\n파트 3: 액티브-스탠바이 복제 설정 1. 주 Harbor에 복제 대상 생성 주 Harbor에 admin으로 로그인하여 관리 → 레지스트리로 이동하고 새 대상 생성:\n스탠바이 정보 입력:\n엔드포인트 URL: http://192.168.8.162 접근 ID / 접근 키: user2 자격 증명 생성 성공:\n2. 복제 정책 생성 관리 → 복제로 이동하여 새 복제 정책 생성:\n대상 레지스트리: Harbor02 선택 모드: 이벤트 기반 (또는 다른 모드) 3. 동기화 결과 확인 Docker 호스트에서 더 많은 이미지를 주 Harbor에 푸시:\n1 2 3 4 5 docker tag httpd:2.4 192.168.8.161/openssh/httpd:2.4 docker push 192.168.8.161/openssh/httpd:2.4 docker tag mysql:5.7 192.168.8.161/openssh/mysql:5.7 docker push 192.168.8.161/openssh/mysql:5.7 주 Harbor 이미지 확인:\n스탠바이 이미지 확인 (자동 동기화됨):\n스탠바이에 주 Harbor에서 동기화된 이미지가 표시되면 액티브-스탠바이 복제가 성공한 것입니다.\n참고: 복제 설정 전에 이미 존재했던 이미지는 수동으로 동기화해야 합니다.\n파트 4: HTTPS 활성화 (자체 서명 인증서) 프로덕션 환경이나 공인 네트워크 사용 시 HTTPS를 권장합니다. 다음은 자체 서명 인증서의 전체 워크플로우입니다.\n1. CA 및 서버 인증서 생성 Harbor 서버에서 실행:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 mkdir -p /data/cert \u0026amp;\u0026amp; cd /data/cert # CA 개인키 생성 openssl genrsa -out ca.key 4096 # CA 인증서 생성 (10년 유효) openssl req -x509 -new -nodes -sha512 -days 3650 \\ -subj \u0026#34;/C=CN/CN=HarborRootCA\u0026#34; \\ -key ca.key \\ -out ca.crt # 서버 개인키 생성 openssl genrsa -out harbor.key 4096 # 서버 인증서 요청 생성 (CN = Harbor IP 또는 도메인) openssl req -sha512 -new \\ -subj \u0026#34;/C=CN/CN=192.168.8.161\u0026#34; \\ -key harbor.key \\ -out harbor.csr 2. 인증서 확장 정보 설정 SAN(Subject Alternative Name)이 포함된 v3.ext 파일 생성:\n1 2 3 4 5 6 7 8 9 10 cat \u0026gt; v3.ext \u0026lt;\u0026lt;-EOF authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] IP.1=192.168.8.161 EOF IP.1은 Harbor의 실제 접속 주소와 일치해야 합니다. 그렇지 않으면 Docker 클라이언트가 인증서 도메인 불일치 오류를 보고합니다.\n3. 서버 인증서 서명 1 2 3 4 5 openssl x509 -req -sha512 -days 3650 \\ -extfile v3.ext \\ -CA ca.crt -CAkey ca.key -CAcreateserial \\ -in harbor.csr \\ -out harbor.crt 4. Harbor 설정에서 HTTPS 활성화 1 vim /usr/local/harbor/harbor.yml https 섹션의 주석을 해제하고 인증서 경로 업데이트:\n1 2 3 4 5 6 hostname: 192.168.8.161 https: port: 443 certificate: /data/cert/harbor.crt private_key: /data/cert/harbor.key hostname은 v3.ext의 IP.1과 일치해야 합니다.\n5. Harbor 재배포 1 2 3 4 cd /usr/local/harbor/ docker-compose down ./prepare docker-compose up -d 주의: docker-compose down -v를 사용하지 마세요. -v는 네임드 볼륨을 삭제하여 Harbor 데이터베이스와 사용자 데이터가 손실됩니다.\n배포 후 https://192.168.8.161로 접속합니다.\n6. Docker 클라이언트 인증서 신뢰 설정 이미지를 푸시/풀해야 하는 모든 머신에서 실행:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 인증서 디렉토리 생성 (이름은 Harbor IP 또는 도메인이어야 함) mkdir -p /etc/docker/certs.d/192.168.8.161 # CA 인증서를 해당 디렉토리에 복사 # 방법 1: Harbor 서버에서 scp scp root@192.168.8.161:/data/cert/ca.crt /etc/docker/certs.d/192.168.8.161/ # 방법 2: 수동으로 ca.crt 파일 내용 복사 # Docker 재시작 systemctl restart docker # 로그인 테스트 docker login 192.168.8.161 # admin과 비밀번호 입력, \u0026#34;Login Succeeded\u0026#34; 표시되면 성공 파트 5: FAQ Q: HTTP 모드와 HTTPS 모드를 전환할 수 있나요?\n네. harbor.yml의 https 섹션을 수정한 후 ./install.sh를 다시 실행하면 됩니다. HTTP에서 HTTPS로 전환하려면 인증서를 먼저 생성해야 하고, HTTPS에서 HTTP로 전환하려면 https 섹션을 주석 처리하면 됩니다.\nQ: Docker 클라이언트가 HTTPS Harbor 로그인 시 \u0026ldquo;x509: certificate relies on legacy Common Name field\u0026rdquo; 오류를 보고할 때\n이는 인증서에 SAN(Subject Alternative Name)이 없기 때문입니다. v3.ext에 IP.1=\u0026lt;Harbor IP\u0026gt;가 포함되어 있고 -extfile v3.ext로 서명했는지 확인하세요.\nQ: 복제 실패를 어떻게 해결하나요?\n주와 스탠바이 간의 네트워크 연결, 대상 레지스트리 자격 증명, 해당 사용자의 대상 프로젝트 쓰기 권한을 확인하세요.\n","permalink":"https://LMDAWN1127.github.io/ko/posts/harbor-master-slave/","summary":"Harbor 엔터프라이즈 Docker 레지스트리의 액티브-스탠바이 아키텍처, HTTP/HTTPS 모드, 자체 서명 인증서, 크로스 호스트 복제를 완전히 다루는 배포 가이드입니다.","title":"Harbor 액티브-스탠바이 배포: 설치부터 HTTPS 및 복제까지"},{"content":"1. OpenStack 핵심 컴포넌트 및 역할 1. HORIZON — 통합 웹 대시보드 서비스 핵심 기능: 클라우드 관리자와 일반 테넌트가 클라우드 리소스를 시각적으로 할당·모니터링할 수 있는 Web 기반 Dashboard 그래픽 관리 콘솔을 제공합니다. 2. NOVA — 컴퓨트 리소스 관리 서비스 핵심 기능: 클라우드 환경의 가상화 컴퓨트 리소스(nova-compute)를 관리·설정합니다. 역할 분담: Controller 제어 노드: nova-api, nova-scheduler, nova-conductor 등의 스케줄링 서비스를 실행합니다. 사용자가 클라우드 인스턴스 생성을 요청하면 nova-api가 요청을 수신·파싱하고, nova-scheduler가 정책에 따라 최적의 컴퓨트 노드를 선택해 부하를 스케줄링합니다. Compute 컴퓨트 노드: 클라우드 인스턴스를 실제로 호스팅하는 워커 노드(KVM, VMware ESXi, Xen 등 이기종 하이퍼바이저 지원). 제어 노드의 고가용성과 성능 병목: 제어 노드는 모든 API 게이트웨이, 데이터베이스, 메시지 큐를 담당하여 고가용성·성능 부하가 발생한다. 3. GLANCE — 이미지 서비스 핵심 기능: OS 이미지의 발견, 등록, 검색 및 스토리지 관리를 담당합니다. 일반적인 디스크 이미지 형식: KVM 일반 형식: qcow2(씬 프로비저닝 및 Copy-on-Write, COW 지원); VMware 일반 형식: vmdk. 배포 권장: 일반적으로 고가용성을 위해 제어 노드에 통합·배치하여 배포합니다. 4. SWIFT — 오브젝트 스토리지 서비스 핵심 기능: 고가용·분산·최종 일관성 객체 스토리지 서비스(Object Storage)를 제공하며, 정적 비정형 데이터, 클라우드 인스턴스 시스템 이미지, 전체 데이터 백업 등에 특화되어 있다. 5. NEUTRON — 소프트웨어 정의 네트워크(SDN) 핵심 기능: 클라우드 환경에서 네트워크 서비스(NaaS)를 제공하며 가상 라우터, 스위치, 보안 그룹, 플로팅 IP, 노드 간 트래픽 연결을 관리합니다. 네트워크 기술: VLAN, VXLAN, Geneve 및 분산 가상 라우팅(DVR)을 완전 지원; 하드웨어 SDN 모드: 프로덕션 환경에서는 보통 2대의 전용 네트워크 노드를 배치해 액티브-액티브 또는 액티브-스탠바이 클러스터를 구성한다. 6. CINDER — 블록 스토리지 서비스 핵심 기능: 클라우드 인스턴스용 영구 블록 스토리지(클라우드 디스크)를 제공하며 동적 연결·분리·스냅샷·확장이 가능하다. 백엔드 연동 능력: 기존 스토리지 어레이 연동: 화웨이 OceanStor, IBM, EMC 등 하이엔드 하드웨어 스토리지; 오픈소스 분산 스토리지 연동: Ceph RBD, GlusterFS 등. 백엔드 메커니즘: 사용자가 클라우드 디스크를 신청하면 Cinder는 지시를 백엔드 스토리지 시스템으로 하위 전달하며, 본질적으로 물리/분산 스토리지 내에 LUN을 할당·매핑한다. 비교 설명: 객체 스토리지(예: 바이두 왕판, Swift, S3)는 블록 디바이스로 직접 포맷·마운트하여 클라우드 인스턴스 파일 시스템으로 쓸 수 없지만, Cinder 블록 디바이스는 직접 포맷해 마운트해 사용할 수 있다. 7. HEAT — 오케스트레이션 서비스 핵심 기능: 선언형 템플릿(HOT 템플릿 또는 AWS CloudFormation 형식)을 기반으로 그래픽 오케스트레이션이나 스크립트를 통해 다수의 VM, 데이터베이스 클러스터, 네트워크 토폴로지, 모니터링 정책을 원클릭으로 자동 일괄 배포한다. 8. CEILOMETER — 계측 모니터링 서비스 핵심 기능: 물리·가상 리소스의 성능 지표, 가동 시간, 사용량을 수집한다. 퍼블릭 클라우드 시나리오: API 호출량, 네트워크 송출량, 컴퓨트 시간을 기록하고 과금 시스템과 연동; 프라이빗 클라우드 시나리오: 테넌트 할당량 수준을 모니터링하고 리소스 과다 사용·유휴를 식별해 용량 계획을 지원한다. 9. KEYSTONE — 통합 인증 및 권한 부여 서비스 핵심 기능: OpenStack 전체 보안의 초석으로, 플랫폼 전반의 테넌트(Project), 사용자(User), 역할(Role), 서비스 카탈로그, 토큰 인증을 담당한다. 엔터프라이즈 아이덴티티 연합: 기업의 기존 Windows Active Directory(AD) 또는 Linux OpenLDAP과 원활히 통합해 엔터프라이즈급 통합 계정 single sign-on(SSO)을 구현한다; 워크플로우 승인 및 권한 격리(화웨이 클라우드 예시): 클라우드 인스턴스 다단계 신청·승인 워크플로우 지원(최대 5단계 승인); \u0026ldquo;삼권 분립\u0026rdquo; 보안 원칙을 관철(보안 담당자, 시스템 관리자, 감사자가 권한을 분리해 관리); 고객사 자체 개발 OA/BPM 승인 흐름 시스템 연동을 지원한다. 핵심 컴포넌트 기능 요약표: 컴포넌트 이름 코드 명칭 핵심 서비스 기능 Horizon Dashboard 통합 Web 그래픽 관리 콘솔 Nova Compute 컴퓨트 리소스 라이프사이클 및 인스턴스 스케줄링 관리 Glance Image VM 이미지 스토리지 및 버전 등록 Swift Object 대규모 비정형 객체 스토리지 서비스 Neutron Network 소프트웨어 정의 네트워크(SDN), 라우팅 및 보안 그룹 관리 Cinder Block 클라우드 인스턴스 영구 블록 스토리지(클라우드 디스크) 관리 Heat Orchestration 인프라-as-Code(IaC) 다중 리소스 스택 오케스트레이션 Ceilometer Metering 리소스 사용량 통계, 모니터링 계량 및 할당량 분석 Keystone Identity 통합 신원 인증, 인가 토큰 및 서비스 카탈로그 관리 2. OpenStack 배포 계획(Victoria 에디션) 1. 배포 방식 비교 오픈소스 수동 배포: 컴포넌트를 하나씩 컴파일·설정—번거롭고 복잡하며 보통 기반 메커니즘 심층 학습용; 도구 기반 자동화 배포: Ansible 배포(예: OpenStack-Ansible, Kolla-Ansible); Packstack 배포: Red Hat가 오픈소스로 제공하는 Puppet 기반 자동화 신속 전달 도구(본 실전에서 채택); 2. 클러스터 노드 사양 및 네트워크 계획 (1) Controller 제어 노드(1대) 탑재 컴포넌트: API 서비스: nova-api, cinder-api, glance-api, neutron-server 등; 기반 서비스: MariaDB / MySQL, RabbitMQ 메시지 큐, Memcached, Keystone; 네트워크 및 관리 서비스: 네트워크 노드 외부 라우팅 포워딩, 로컬 YUM 캐시, Chrony 시간 동기화 서버. 시스템 사양 요구: CPU: 4 vCPU 이상 메모리: 4GB 이상 디스크: 100GB 사용 가능 디스크 공간 NIC: 네트워크 카드 3개(NIC 1은 인터넷 접속용 브릿지 모드, NIC 2·3은 호스트 전용 모드로 내부 트래픽) IP 주소 계획: 내부 관리 IP: 192.168.100.10 외부 접속 IP: 192.168.10.10 (2) Compute 컴퓨트 노드(1대) 탑재 컴포넌트: nova-compute, 네트워크 L2 Agent(Open vSwitch Agent) 시스템 사양 요구: CPU: 4 vCPU 이상 메모리: 8GB 이상(최소 4GB) 디스크: 100GB 디스크 공간 NIC: 네트워크 카드 3개(전부 호스트 전용 모드) IP 주소 계획: Compute: 192.168.100.11 (3) YUM 저장소 서버 + Chrony 시간 동기화 서버(1대) 시스템 사양 요구: CPU: 2 vCPU 메모리: 2GB NIC: 네트워크 카드 2개(하나는 인터넷 브릿지로 공개 시각 동기화 및 상위 저장소용, 다른 하나는 호스트 전용으로 내부 노드 제공) IP 주소 계획: 외부 IP: 192.168.10.11 내부 IP: 192.168.100.20 환경 하향 조정 안내: 개인용 컴퓨터 하드웨어 자원이 부족하면 최소 2대의 VM으로 압축할 수 있다(제어 노드를 YUM 소스 및 시간 동기화 서버로 겸용하고, 컴퓨트 노드 1대를 추가). 주의: 모든 노드의 시스템 시각은 매우 일치해야 하며 시계 드리프트는 5분을 초과해서는 안 된다. 그렇지 않으면 Keystone 핸드셰이크와 메시지 큐 통신이 완전히 실패한다.\n3. OpenStack 자동화 배포 실전 절차 단계 1: YUM 저장소 서버 및 Chrony 시간 동기화 서버 구축 1. 네트워크 인터페이스 설정 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 # 1. 내부 통신 NIC ens160 설정 [root@Cloud ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens160 TYPE=Ethernet BOOTPROTO=none NAME=ens160 DEVICE=ens160 ONBOOT=yes IPADDR=192.168.100.20 PREFIX=24 # 2. 인터넷 연결용 NIC ens192 설정(공개 NTP 동기화용) [root@Cloud ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens192 TYPE=Ethernet BOOTPROTO=none NAME=ens192 DEVICE=ens192 ONBOOT=yes PROXY_METHOD=none BROWSER_ONLY=no IPADDR=192.168.10.11 PREFIX=24 GATEWAY=192.168.10.1 DNS1=192.168.10.1 DEFROUTE=yes IPV4_FAILURE_FATAL=no IPV6INIT=no UUID=03da7500-2101-c722-2438-d0d006c28c73 # 설정을 적용하기 위해 NIC 재시작 [root@Cloud ~]# nmcli connection down ens160 ; nmcli connection up ens160 Connection \u0026#39;ens160\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/1) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4) [root@Cloud ~]# nmcli connection down ens192 ; nmcli connection up ens192 Connection \u0026#39;ens192\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/3) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/5) 2. 로컬 CD-ROM YUM 저장소 설정 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@Cloud ~]# cd /etc/yum.repos.d/ [root@Cloud yum.repos.d]# mkdir bak [root@Cloud yum.repos.d]# mv CentOS-Linux-* bak/ # OS 설치 ISO 마운트 mount /dev/cdrom /media/ # CD-ROM 로컬 저장소 설정 파일 작성 [root@Cloud ~]# cat \u0026gt; /etc/yum.repos.d/dvd.repo \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; \u0026gt; [BaseOS] \u0026gt; name=BaseOS \u0026gt; baseurl=file:///media/BaseOS \u0026gt; gpgcheck=0 \u0026gt; \u0026gt; [AppStream] \u0026gt; name=AppStream \u0026gt; baseurl=file:///media/AppStream \u0026gt; gpgcheck=0 \u0026gt; EOF 3. Chrony 시간 동기화 서버 설정 OpenStack은 노드 간 시각 동기화 요구가 매우 엄격하므로 먼저 시간 소스를 설정한다:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # /etc/chrony.conf 편집 [root@Cloud ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool ntp.tencent.com iburst # Allow NTP client access from local network. allow 192.168.100.0/24 # Serve time even if not synchronized to a time source. local stratum 10 # 시간 동기화 서비스 재시작 및 검증 [root@Cloud ~]# systemctl restart chronyd.service [root@Cloud ~]# chronyc sources 210 Number of sources = 1 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^* 106.55.184.199 2 6 17 2 -224us[ -144us] +/- 54ms # 방화벽 및 SELinux 비활성화 [root@Cloud ~]# systemctl disable firewalld.service --now [root@Cloud ~]# setenforce 0 [root@Cloud ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config 4. httpd 기반 로컬 웹 YUM 저장소 구축 CentOS 8 및 OpenStack Victoria 소프트웨어 저장소를 HTTP 미러 소스로 게시하여 모든 클러스터 노드가 가져오도록 한다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 # 1. Apache 웹 서비스 설치 및 시작 [root@Cloud ~]# yum -y install httpd [root@Cloud ~]# systemctl enable httpd --now # 2. 디렉터리 생성 및 이미지 파일 마운트 [root@Cloud ~]# mkdir /isos [root@Cloud ~]# mkdir -p /var/www/html/centos8 [root@Cloud ~]# mkdir -p /var/www/html/openstack # 베이스 시스템 CD 및 OpenStack Victoria 설치 이미지 마운트 [root@Cloud ~]# mount /dev/cdrom /var/www/html/centos8/ [root@Cloud ~]# mount /isos/26-CentOS8-4-OSP-Victoria.iso /var/www/html/openstack/ # 3. 마운트를 /etc/fstab에 영구 기록 [root@Cloud ~]# vim /etc/fstab /dev/cdrom /var/www/html/centos8 iso9660 defaults 0 0 /isos/26-CentOS8-4-OSP-Victoria.iso /var/www/html/openstack iso9660 defaults 0 0 # 4. HTTP YUM 저장소 파일 설정 [root@Cloud ~]# vim /etc/yum.repos.d/dvd.repo [BaseOS] name=BaseOS baseurl=http://192.168.100.20/centos8/BaseOS gpgcheck=0 [AppStream] name=AppStream baseurl=http://192.168.100.20/centos8/AppStream gpgcheck=0 [root@Cloud ~]# vim /etc/yum.repos.d/openstack.repo [centos-advanced-virtualization] name=centos-advanced-virtualization baseurl=http://192.168.100.20/openstack/centos-advanced-virtualization gpgcheck=0 [centos-nfv-openvswitch] name=centos-nfv-openvswitch baseurl=http://192.168.100.20/openstack/centos-nfv-openvswitch gpgcheck=0 [centos-rabbitmq-38] name=centos-rabbitmq-38 baseurl=http://192.168.100.20/openstack/centos-rabbitmq-38 gpgcheck=0 [PowerTools] name=PowerTools baseurl=http://192.168.100.20/openstack/PowerTools gpgcheck=0 [centos-ceph-nautilus] name=centos-ceph-nautilus baseurl=http://192.168.100.20/openstack/centos-ceph-nautilus gpgcheck=0 [centos-openstack-victoria] name=centos-openstack-victoria baseurl=http://192.168.100.20/openstack/centos-openstack-victoria gpgcheck=0 [extras] name=extras baseurl=http://192.168.100.20/openstack/extras gpgcheck=0 # 5. 작성한 repo 파일을 각 제어 노드와 컴퓨트 노드에 일괄 배포 [root@Cloud ~]# scp /etc/yum.repos.d/dvd.repo /etc/yum.repos.d/openstack.repo root@192.168.100.10:/etc/yum.repos.d/ [root@Cloud ~]# scp /etc/yum.repos.d/dvd.repo /etc/yum.repos.d/openstack.repo root@192.168.100.11:/etc/yum.repos.d/ 단계 2: 클러스터 각 노드 시스템 환경 초기화 1. Controller 제어 노드 초기화(192.168.100.10) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 # 1. 관리 네트워크 ens160 설정 [root@Controller ~]# nmcli connection modify ens160 ipv4.addresses 192.168.100.10/24 ipv4.method manual autoconnect yes [root@Controller ~]# nmcli connection down ens160 ; nmcli connection up ens160 # 2. 외부 네트워크 NIC ens224 설정 [root@Controller ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens224 TYPE=Ethernet BOOTPROTO=none NAME=ens224 DEVICE=ens224 ONBOOT=yes [root@Controller ~]# nmcli connection reload [root@Controller ~]# nmcli connection modify ens224 ipv4.addresses 192.168.10.10/24 ipv4.gateway 192.168.10.1 ipv4.dns 192.168.10.1 ipv4.method manual autoconnect yes [root@Controller ~]# nmcli connection down ens224 ; nmcli connection up ens224 # 3. Chrony NTP 클라이언트 설정 [root@Controller ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool 192.168.100.20 iburst [root@Controller ~]# systemctl restart chronyd.service [root@Controller ~]# chronyc sources 210 Number of sources = 1 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^* 192.168.100.20 3 6 377 24 -22us[ -46us] +/- 43ms # 4. 호스트명 설정 [root@Controller ~]# hostnamectl set-hostname Controller # 5. 방화벽 및 SELinux 비활성화 [root@Controller ~]# systemctl disable firewalld.service --now [root@Controller ~]# setenforce 0 [root@Controller ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config # 6. 클러스터 /etc/hosts 해석 설정 [root@Controller ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 7. 기본 시스템 repo 캐시 정리 [root@Controller ~]# cd /etc/yum.repos.d [root@Controller yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ 2. Compute 컴퓨트 노드 초기화(192.168.100.11) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # 1. 관리 네트워크 설정 [root@Compute ~]# nmcli connection modify ens160 ipv4.addresses 192.168.100.11/24 ipv4.method manual autoconnect yes [root@Compute ~]# nmcli connection down ens160 ; nmcli connection up ens160 # 2. Chrony NTP 클라이언트 설정 [root@Compute ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool 192.168.100.20 iburst [root@Compute ~]# systemctl enable chronyd.service --now # 3. 호스트명 설정 [root@Compute ~]# hostnamectl set-hostname Compute # 4. 방화벽 및 SELinux 비활성화 [root@Compute ~]# systemctl disable firewalld.service --now [root@Compute ~]# setenforce 0 [root@Compute ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config # 5. 클러스터 hosts 해석 설정 [root@Compute ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 6. 기본 시스템 repo 정리 [root@Compute ~]# cd /etc/yum.repos.d [root@Compute yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ ⚠️ 핵심 점검점: 이 시점에서 모든 VM을 종료하고 문제 발생 시 신속히 롤백할 수 있도록 통합 스냅샷을 촬영할 것을 권장한다.\n단계 3: Packstack 자동화 응답 파일 배포 1. Packstack 배포 도구 설치 제어 노드(Controller)에서 실행:\n1 [root@Controller ~]# yum -y install openstack-packstack 2. 기본 응답 파일 생성 1 [root@Controller ~]# packstack --gen-answer-file=/root/answers.txt 네트워크 방식 설명: OpenStack Victoria는 OVN(Open Virtual Network)과 기존 OVS(Open vSwitch) 모두를 지원한다.\n3. 응답 파일 파라미터 정밀 커스터마이징 1 2 # 자동 감지된 외부 IP를 내부 관리 IP 192.168.100.10으로 일괄 치환 [root@Controller ~]# sed -i \u0026#39;s/192.168.10.10/192.168.100.10/g\u0026#39; /root/answers.txt /root/answers.txt를 편집하고 다음 핵심 설정 항목을 확인·수정한다:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # 1. 컴퓨트 노드 클러스터 IP 목록 지정(쉼표 구분) CONFIG_COMPUTE_HOSTS=192.168.100.11 # 2. Keystone 관리자 root 초기 비밀번호 설정 CONFIG_KEYSTONE_ADMIN_PW=000000 # 3. Demo 테넌트 네트워크 비활성화(프로덕션 규범) CONFIG_PROVISION_DEMO=n # 4. Heat 오케스트레이션 컴포넌트 지원 활성화 CONFIG_HEAT_INSTALL=y # 5. Neutron 코어 드라이버 및 네트워크 유형 설정(ML2 + OVS) CONFIG_NEUTRON_ML2_TYPE_DRIVERS=geneve,flat,vxlan,vlan CONFIG_NEUTRON_ML2_MECHANISM_DRIVERS=openvswitch CONFIG_NEUTRON_ML2_TENANT_NETWORK_TYPES=vxlan CONFIG_NEUTRON_L2_AGENT=openvswitch # 6. OVS 외부 네트워크 브리지 바인딩 설정(물리 외부 NIC ens224 연결) CONFIG_NEUTRON_OVS_BRIDGE_MAPPINGS=extnet:br-ex CONFIG_NEUTRON_OVS_BRIDGE_IFACES=br-ex:ens224 # 7. 사전 스토리지 할당량 용량 설정 CONFIG_SWIFT_STORAGE_SIZE=20G CONFIG_CINDER_VOLUMES_SIZE=50G ⚠️ 핵심 점검점: 응답 파일 수정 후, 다시 모든 VM을 종료하고 스냅샷을 촬영할 것을 권장한다.\n4. 원클릭 자동화 배포 실행 전원 켠 후 제어 노드에서 배포 명령 실행:\n1 [root@Controller ~]# packstack --answer-file=/root/answers.txt 배포 스크립트는 Puppet을 기반으로 모든 노드에서 소프트웨어 소스 설정, 데이터베이스 테이블 생성, 메시지 큐 클러스터 구축, 인증서 생성, 각 컴포넌트 협조 통합 테스트를 자동으로 완료한다. 완료되면 터미널에 Dashboard 로그인 URL과 keystonerc_admin 환경 변수 파일 경로를 출력한다.\n4. Linux 시스템 로그 관리 및 OpenStack 장애 해결 1. Linux Rsyslog 로그 레벨 및 설정 아키텍처 Linux 시스템에서 시스템 하위 계층과 대부분의 서비스 로그는 rsyslog 데몬에 의해 중앙 집중 관리된다.\n설정 파일 확인: 1 2 rpm -qc rsyslog # 핵심 메인 설정 파일: /etc/rsyslog.conf 로그 규칙 구문 분석: 1 서비스 유형(Facility).로그 레벨(Priority) 로그 저장 경로 일반적인 설정 예시:\n1 2 3 4 5 6 7 8 9 # 1. 시스템 일반 메시지: # 모든 서비스의 \u0026gt;=info 레벨 로그를 기록하되 메일, 인증 보안, cron 로그 제외 *.info;mail.none;authpriv.none;cron.none /var/log/messages # 2. 디버그 로그: *.debug /var/log/debug # 3. 오류 수준 로그: *.err /var/log/error.log 구문 규칙 핵심 사항: mail: 서비스 유형(Facility); info: 로그 우선순위 레벨; .: 이상(\u0026gt;=) 의 우선순위 로그 메시지에 일치함을 나타냄; *.info: 모든 서비스의 \u0026gt;= info 레벨 로그가 대상 파일에 집계되어 기록됨을 의미한다. 실시간 동적 로그 모니터링 명령: 1 2 3 4 5 # 시스템 로그 끝 출력 추적 tail /var/log/messages # 실시간 로그 스트림 지속 수신 tail -f /var/log/messages 2. OpenStack 커스텀 서비스 로그 트러블슈팅 가이드 트러블슈팅 원칙: 각 컴포넌트는 보통 독립적인 커스텀 로그 파일 체계(/var/log/\u0026lt;컴포넌트명\u0026gt;/ 하위)를 갖는다. 컴포넌트에 이상이 있으면 먼저 해당 컴포넌트 전용 로그를 확인하고, 커스텀 로그가 없을 때만 시스템 전역 로그 /var/log/messages로 돌아가 확인한다.\n실전 사례: Neutron 네트워크 서비스 장애 위치 확인 배포 또는 운영 중 가상 네트워크 생성이 실패하거나 OVS 포트가 올라오지 않으면, 정규식 매칭으로 오류와 경고를 직접 포착할 수 있다:\n1 2 3 4 5 # 1. 모든 Neutron 로그에서 정규식으로 오류·경고 필터링 tail -f /var/log/neutron/*.log | grep -iE \u0026#39;(err|warn)\u0026#39; # 2. neutron-server 핵심 API 서비스 로그를 심층 확인하고 매칭 항목 전후 3줄 컨텍스트 출력: tail -f /var/log/neutron/server.log | grep -iE -A3 -B3 \u0026#39;(err|warn)\u0026#39; 파라미터 설명:\n-i: 대소문자 무시; -E: 확장 정규식 매칭 (err|warn) 활성화; -A 3 (After): 매칭 행 이후 3줄 출력; -B 3 (Before): 매칭 행 이전 3줄 출력. ","permalink":"https://LMDAWN1127.github.io/ko/posts/openstack-core-architecture-deployment/","summary":"Nova/Glance/Neutron/Cinder 등 핵심 컴포넌트부터 Packstack 기반 Victoria 릴리스 원클릭 자동 배포, 그리고 Rsyslog 로그 트러블슈팅까지.","title":"OpenStack 클라우드 OS: 핵심 아키텍처 및 자동화 배포"},{"content":"Cinder 볼륨 생성 및 연결 아키텍처 볼륨 생성은 cinder 노드(컨트롤러 노드)가 담당하고, 볼륨 마운트는 nova가 담당합니다\n로컬 LVM + iSCSI 볼륨 스토리지 구성 answers.txt 응답 파일에서 cinder 볼륨을 생성하도록 지정합니다. 실습 환경에서는 전용 스토리지 장비를 제공할 수 없으므로 시스템은 로컬 LVM을 cinder 스토리지로 사용합니다 시스템은 기본적으로 cinder-volumes 볼륨 그룹을 50G 크기로 생성합니다(사용자가 수동 지정)\n이 볼륨 그룹은 컨트롤러 노드에 생성됩니다\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [root@Controller ~]# vgdisplay --- Volume group --- VG Name cl System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 4 VG Access read/write VG Status resizable MAX LV 0 Cur LV 3 Open LV 3 Max PV 0 Cur PV 1 Act PV 1 VG Size \u0026lt;99.00 GiB PE Size 4.00 MiB Total PE 25343 Alloc PE / Size 25343 / \u0026lt;99.00 GiB Free PE / Size 0 / 0 VG UUID K2c9TX-MlXI-k9Ga-CPPm-3zuu-WNoK-mRuUEZ --- Volume group --- VG Name cinder-volumes System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 4 VG Access read/write VG Status resizable MAX LV 0 Cur LV 1 Open LV 0 Max PV 0 Cur PV 1 Act PV 1 VG Size \u0026lt;51.50 GiB PE Size 4.00 MiB Total PE 13183 Alloc PE / Size 12551 / \u0026lt;49.03 GiB Free PE / Size 632 / \u0026lt;2.47 GiB VG UUID nXhaeU-1PwG-is18-Ef2N-cK5R-9LRk-3ksn1k /etc/cinder/cinder.conf에서 LVM 드라이버를 호출해 생성된 볼륨 그룹을 인식시킵니다 이 컨트롤러 노드를 ISCSI 스토리지(target)로 사용하여 사용자가 만든 볼륨을 ISCSI로 compute 노드에 공유합니다 사용자가 2G 클라우드 디스크를 생성하면 시스템이 자동으로 LVM 드라이버를 호출해 볼륨 그룹 안에 2G LV를 만듭니다\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 [root@Controller ~]# lvdisplay --- Logical volume --- LV Path /dev/cl/swap LV Name swap VG Name cl LV UUID f9Aqve-GgxT-gQNg-v0W0-ogyz-1cJT-WIEOMw LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 2 LV Size \u0026lt;7.88 GiB Current LE 2016 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:1 --- Logical volume --- LV Path /dev/cl/home LV Name home VG Name cl LV UUID 5f8ntZ-4pxS-yYFl-3BHC-qKaW-pFsi-2ZGkHg LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 1 LV Size 29.89 GiB Current LE 7653 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:2 --- Logical volume --- LV Path /dev/cl/root LV Name root VG Name cl LV UUID 32JpBQ-Ld4z-0bLU-jHuG-iMRC-WLoO-WtcrnT LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 1 LV Size \u0026lt;61.23 GiB Current LE 15674 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:0 --- Logical volume --- LV Name cinder-volumes-pool VG Name cinder-volumes LV UUID ughXDw-Fl7B-Mbo9-vsHS-OvMy-Pi60-E2GPaa LV Write Access read/write (activated read only) LV Creation host, time Controller, 2026-07-08 16:03:29 +0800 LV Pool metadata cinder-volumes-pool_tmeta LV Pool data cinder-volumes-pool_tdata LV Status available # open 0 LV Size \u0026lt;48.93 GiB Allocated pool data 0.00% Allocated metadata 10.44% Current LE 12525 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:6 --- Logical volume --- LV Path /dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e LV Name volume-3561d822-2929-4b47-87c5-25f769f8f44e VG Name cinder-volumes LV UUID WoSBug-zWc4-wVYn-39xk-MWVw-z3Pj-UOdvok LV Write Access read/write LV Creation host, time Controller, 2026-10-05 15:56:30 +0800 LV Pool name cinder-volumes-pool LV Status available # open 0 LV Size 2.00 GiB Mapped size 0.00% Current LE 512 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:7 사용자가 이 볼륨을 클라우드 서버 web01에 연결하면 cinder가 스토리지 측에 명령을 보냅니다. 스토리지 측은 해당 LV를 compute 호스트에 공유하고, compute 호스트가 그 디스크를 인식해 새로운 2G 디스크가 생기며 libvirt가 xml 파일을 호출해 클라우드 서버에 추가합니다\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 [root@Controller ~]# systemctl status target ● target.service - Restore LIO kernel target configuration Loaded: loaded (/usr/lib/systemd/system/target.service; enabled; vendor preset: disabled) Active: active (exited) since Mon 2026-10-05 14:18:51 CST; 1h 30min ago Process: 1873 ExecStart=/usr/bin/targetctl restore (code=exited, status=0/SUCCESS) Main PID: 1873 (code=exited, status=0/SUCCESS) Tasks: 0 (limit: 36406) Memory: 0B CGroup: /system.slice/target.service Oct 05 14:18:50 Controller systemd[1]: Starting Restore LIO kernel target configuration... Oct 05 14:18:51 Controller target[1873]: No saved config file at /etc/target/saveconfig.json, ok, exiting Oct 05 14:18:51 Controller systemd[1]: Started Restore LIO kernel target configuration. [root@Controller ~]# targetcli targetcli shell version 2.1.53 Copyright 2011-2013 by Datera, Inc and others. For help on commands, type \u0026#39;help\u0026#39;. /\u0026gt; ls o- / ......................................................................................................................... [...] o- backstores .............................................................................................................. [...] | o- block .................................................................................................. [Storage Objects: 1] | | o- iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e [/dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e (2.0GiB) write-thru activated] | | o- alua ................................................................................................... [ALUA Groups: 1] | | o- default_tg_pt_gp ....................................................................... [ALUA state: Active/optimized] | o- fileio ................................................................................................. [Storage Objects: 0] | o- pscsi .................................................................................................. [Storage Objects: 0] | o- ramdisk ................................................................................................ [Storage Objects: 0] o- iscsi ............................................................................................................ [Targets: 1] | o- iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e ............................................. [TPGs: 1] | o- tpg1 .......................................................................................... [no-gen-acls, auth per-acl] | o- acls .......................................................................................................... [ACLs: 1] | | o- iqn.1994-05.com.redhat:1220933cde3c ...................................................... [1-way auth, Mapped LUNs: 1] | | o- mapped_lun0 ................. [lun0 block/iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (rw)] | o- luns .......................................................................................................... [LUNs: 1] | | o- lun0 [block/iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (/dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e) (default_tg_pt_gp)] | o- portals .................................................................................................... [Portals: 1] | o- 192.168.100.10:3260 .............................................................................................. [OK] o- loopback ......................................................................................................... [Targets: 0] /\u0026gt; [root@Compute ~]# cat /etc/iscsi/initiatorname.iscsi InitiatorName=iqn.1994-05.com.redhat:1220933cde3c [root@Compute ~]# iscsiadm -m session tcp: [1] 192.168.100.10:3260,1 iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (non-flash) [root@Compute ~]# vim /etc/libvirt/qemu/instance-00000002.xml \u0026lt;disk type=\u0026#39;block\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;raw\u0026#39; cache=\u0026#39;none\u0026#39; io=\u0026#39;native\u0026#39;/\u0026gt; \u0026lt;source dev=\u0026#39;/dev/sda\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vdb\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;serial\u0026gt;3561d822-2929-4b47-87c5-25f769f8f44e\u0026lt;/serial\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x00\u0026#39; slot=\u0026#39;0x07\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; 스토리지 측 설정\ntargetcli 패키지를 설치하고 서비스를 시작합니다 1 2 3 4 5 6 7 8 9 #yum -y install targetcli #systemctl enable target --now #targetcli /\u0026gt; /backstores/block create dev=/dev/sdb1 name=data01 /\u0026gt; /iscsi create iqn.2024.com.storage:disk /\u0026gt; /iscsi/ iqn.2024.com.storage:disk/tpg1/acls create iqn.1994-05.com.redhat:b4ff583759f4 /\u0026gt;/iscsi/ iqn.2024.com.storage:disk /tpg1/luns create data01 /\u0026gt; /iscsi/ iqn.2024.com.storage:disk/tpg1/portals create 192.168.100.10:3260 /\u0026gt; saveconfig 타깃, 서비스 서버, 스토리지 클라이언트 1 2 3 [root@Compute ~]# iscsiadm -m discovery -t st -p 192.168.100.10 [root@Compute ~]# iscsiadm -m node -T iqn.2024.com.storage:disk -p 192.168.100.10:3260 -l #fdisk -l Cinder와 NFS 스토리지 연동 위 그림에 따라 네트워크를 계획하고 구성합니다\n1. NFS 스토리지 구성 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #配置网络 [root@storage ~]# nmcli connection modify ens160 ipv4.addresses 192.168.200.20/24 ipv4.method manual autoconnect yes [root@storage ~]# nmcli connection down ens160 ; nmcli connection up ens160 [root@storage ~]# cd /etc/sysconfig/network-scripts/ [root@storage network-scripts]# cp ifcfg-ens160 ifcfg-ens192 [root@storage network-scripts]# cat ifcfg-ens192 # Generated by parse-kickstart TYPE=Ethernet DEVICE=ens192 ONBOOT=yes BOOTPROTO=none NAME=ens192 [root@storage ~]# nmcli connection reload [root@storage ~]# nmcli connection modify ens192 ipv4.addresses 192.168.8.20/24 ipv4.method manual autoconnect yes [root@storage ~]# nmcli connection down ens192 ; nmcli connection up ens192 #安装配置安装NFS服务 [root@storage ~]# yum install -y nfs-utils rpcbind [root@storage ~]# mkdir /storage [root@storage ~]# cat /etc/exports /storage 192.168.200.0/24(rw,sync,no_root_squash) /storage 192.168.8.0/24(rw,sync,no_root_squash) [root@storage ~]# systemctl enable nfs-server --now [root@storage ~]# systemctl enable rpcbind --now 2. cinder 노드 네트워크 구성(컨트롤러 노드) 1 2 3 4 5 6 7 8 [root@Controller ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens192 TYPE=Ethernet NAME=ens192 DEVICE=ens192 ONBOOT=yes IPADDR=192.168.8.10 NETMASK=255.255.255.0 [root@Controller ~]# systemctl restart network 3. 컴퓨트 노드 네트워크 구성 1 2 [root@Compute ~]# nmcli connection modify ens192 ipv4.addresses 192.168.200.10/24 autoconnect yes ipv4.method manual [root@Compute ~]# nmcli connection down ens192 ; nmcli connection up ens192 4. cinder 구성 1 2 3 4 5 6 7 8 9 10 [root@Controller ~]# vim /etc/cinder/cinder.conf 410 default_volume_type=nfs 436 enabled_backends=lvm,nfs666 [nfs666] volume_backend_name=nfsdata volume_driver=cinder.volume.drivers.nfs.NfsDriver nfs_shares_config = /etc/cinder/nfs_shares [root@Controller ~]# cat /etc/cinder/nfs_shares storage:/storage [root@Controller ~]# systemctl restart openstack-cinder-* Cinder 노드\n1 2 [root@Controller ~]# cat /etc/hosts ####额外增加一条记录 192.168.8.20 storage 컴퓨트 노드\n1 2 3 4 5 6 [root@Compute ~]# cat /etc/hosts 192.168.200.20 storage [root@Controller ~]# source keystonerc_admin [root@Controller ~(keystone_admin)]# cinder type-create nfs [root@Controller ~(keystone_admin)]# cinder type-key nfs set volume_backend_name=nfsdata [root@Controller ~(keystone_admin)]# systemctl restart openstack-cinder-* OpenStack 웹 화면에서 볼륨을 만들고 nfs를 선택한 뒤 해당 볼륨을 클라우드 서버에 연결합니다\nOpenStack 웹 화면에서 볼륨을 만들고 nfs를 선택한 뒤 소스를 이미지로 지정합니다. 클라우드 서버 생성 시에는 방금 만든 볼륨을 소스로 선택합니다\n클라우드 서버를 만들고 나면 볼륨 하나가 생성됩니다\n기존 클라우드 서버를 삭제하고 다시 생성하면 이 볼륨을 사용할 수 있습니다\n","permalink":"https://LMDAWN1127.github.io/ko/posts/openstack-cinder-block-storage/","summary":"OpenStack Cinder 블록 스토리지 정리: 볼륨 생성과 마운트의 역할 분담(Cinder는 생성, Nova는 마운트), 로컬 LVM + iSCSI로 컨트롤러를 스토리지로 만드는 전체 흐름(VG/LV, targetcli 기반 ISCSI 공유, libvirt 마운트), 그리고 NFS 백엔드 연동과 대시보드 실습.","title":"OpenStack Cinder 블록 스토리지 상세 정리: 로컬 LVM + iSCSI 볼륨과 NFS 백엔드 연동"},{"content":"Keystone 서비스 OpenStack 다중 도메인 지원\n1 2 [root@Controller ~]# vim /etc/openstack-dashboard/local_settings OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True 시스템의 모든 서비스 나열\n1 [root@Controller ~(keystone_admin)]# openstack service list Keystone 주소 및 API 인터페이스\n1 OS_AUTH_URL=http://192.168.100.10:5000/v3 모든 서비스는 Keystone 주소를 알고 있습니다. 사용자 토큰이 유효한지 Keystone에 검증을 요청해야 하기 때문입니다 Keystone은 모든 서비스의 주소를 알고 있습니다. 모든 서비스는 생성 시 Keystone에 등록하기 때문입니다 엔드포인트 주소\n1 [root@Controller ~(keystone_admin)]# openstack endpoint list 각 서비스에는 엔드포인트 주소 3개가 있으며, 이 3개의 URL은 같을 수도 다를 수도 있습니다\npublic url — 외부 사용자가 접근하는 URL 주소\nadmin url — 관리자가 접근하는 URL 주소\ninternal url — 내부 컴포넌트 간 접근용 URL 주소\n클라우드 서버 제공 흐름\n사용자가 Keystone에 사용자 이름과 비밀번호로 인증을 요청하고, 인증에 성공하면 Keystone이 토큰을 발급합니다. 사용자는 그 토큰으로 nova-api에 클라우드 서버 생성 요청을 보냅니다 nova-api는 Keystone에 도움을 요청해 해당 토큰이 유효한지 검증하고, 성공하면 결과를 반환합니다 nova-api가 요청을 nova-compute 호스트로 전달합니다 nova-compute 호스트가 사용자 요청 정보에 따라 클라우드 서버를 생성하고 결과를 nova-api에 반환합니다 nova-api가 결과를 사용자에게 반환하고, 클라우드 서버 생성이 완료되며 해당 호스트 정보는 데이터베이스에 기록됩니다 nova-api에 서버 생성 요청을 보낼 때 저는 nova-api 주소를 모릅니다. 하지만 Keystone 주소는 알고 있습니다. Keystone에 nova-api 주소를 질의합니다. Keystone은 모든 서비스의 엔드포인트 주소를 저장하고 있으므로, 저는 Keystone 주소만 알면 됩니다. 다음은 Keystone 주소를 확인하고 이를 통해 다른 주소를 조회하는 방법입니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 [root@Controller ~(keystone_zlm)]# grep -E \u0026#34;^connection\\s*=.*keystone\u0026#34; /etc/keystone/*.conf /etc/keystone/keystone.conf:connection=mysql+pymysql://keystone_admin:c3fa63522e1f4586@192.168.100.10/keystone # 用户名：keystone_admin # 密码：c3fa63522e1f4586 [root@Controller ~(keystone_zlm)]# mysql -u keystone_admin -p -D keystone Enter password: Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 15494 Server version: 10.3.27-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [keystone]\u0026gt; SELECT s.type AS service_type, -\u0026gt; e.interface, -\u0026gt; e.url -\u0026gt; FROM endpoint e -\u0026gt; JOIN service s ON e.service_id = s.id -\u0026gt; ORDER BY s.type, e.interface; +----------------+-----------+--------------------------------------------------+ | service_type | interface | url | +----------------+-----------+--------------------------------------------------+ | alarming | admin | http://192.168.100.10:8042 | | alarming | internal | http://192.168.100.10:8042 | | alarming | public | http://192.168.100.10:8042 | | cloudformation | admin | http://192.168.100.10:8000/v1 | | cloudformation | internal | http://192.168.100.10:8000/v1 | | cloudformation | public | http://192.168.100.10:8000/v1 | | compute | admin | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | compute | internal | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | compute | public | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | identity | admin | http://192.168.100.10:5000 | | identity | internal | http://192.168.100.10:5000 | | identity | public | http://192.168.100.10:5000 | | image | admin | http://192.168.100.10:9292 | | image | internal | http://192.168.100.10:9292 | | image | public | http://192.168.100.10:9292 | | metering | admin | http://192.168.100.10:8777 | | metering | internal | http://192.168.100.10:8777 | | metering | public | http://192.168.100.10:8777 | | metric | admin | http://192.168.100.10:8041 | | metric | internal | http://192.168.100.10:8041 | | metric | public | http://192.168.100.10:8041 | | network | admin | http://192.168.100.10:9696 | | network | internal | http://192.168.100.10:9696 | | network | public | http://192.168.100.10:9696 | | object-store | admin | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | object-store | internal | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | object-store | public | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | orchestration | admin | http://192.168.100.10:8004/v1/%(tenant_id)s | | orchestration | internal | http://192.168.100.10:8004/v1/%(tenant_id)s | | orchestration | public | http://192.168.100.10:8004/v1/%(tenant_id)s | | placement | admin | http://192.168.100.10:8778/placement | | placement | internal | http://192.168.100.10:8778/placement | | placement | public | http://192.168.100.10:8778/placement | | volumev2 | admin | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev2 | internal | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev2 | public | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev3 | admin | http://192.168.100.10:8776/v3/%(tenant_id)s | | volumev3 | internal | http://192.168.100.10:8776/v3/%(tenant_id)s | | volumev3 | public | http://192.168.100.10:8776/v3/%(tenant_id)s | +----------------+-----------+--------------------------------------------------+ 39 rows in set (0.001 sec) MariaDB [keystone]\u0026gt; SELECT s.type AS service_type, -\u0026gt; e.interface, -\u0026gt; e.url -\u0026gt; FROM endpoint e -\u0026gt; JOIN service s ON e.service_id = s.id -\u0026gt; WHERE e.interface = \u0026#39;public\u0026#39; -\u0026gt; ORDER BY s.type; +----------------+-----------+--------------------------------------------------+ | service_type | interface | url | +----------------+-----------+--------------------------------------------------+ | alarming | public | http://192.168.100.10:8042 | | cloudformation | public | http://192.168.100.10:8000/v1 | | compute | public | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | identity | public | http://192.168.100.10:5000 | | image | public | http://192.168.100.10:9292 | | metering | public | http://192.168.100.10:8777 | | metric | public | http://192.168.100.10:8041 | | network | public | http://192.168.100.10:9696 | | object-store | public | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | orchestration | public | http://192.168.100.10:8004/v1/%(tenant_id)s | | placement | public | http://192.168.100.10:8778/placement | | volumev2 | public | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev3 | public | http://192.168.100.10:8776/v3/%(tenant_id)s | +----------------+-----------+--------------------------------------------------+ 13 rows in set (0.000 sec) MariaDB [keystone]\u0026gt; OpenStack 배포 시 서비스 생성\n데이터베이스에 들어가 데이터베이스(예: glance)를 생성합니다 데이터베이스 사용자 glance를 생성하고, glance 데이터베이스의 모든 테이블에 대한 ALL(조회·추가·수정·삭제) 권한을 부여합니다 glance 서비스를 생성하고 glance-api 주소(엔드포인트 주소)를 데이터베이스에 등록합니다 사용자가 glance-api에 접근해야 할 때 Keystone에 요청을 보내고, Keystone 데이터베이스에서 glance api 엔드포인트 주소를 조회합니다\n1 2 #示例 如果将zlm用户加入admin角色，那他就拥有了admin角色权限 [root@Controller ~(keystone_admin)]# openstack role add --user zlm --project LMandMY_HOME admin 사용자가 nova-api에 요청을 보내면 nova-api는 Keystone에 사용자 신원과 토큰 유효 기간을 검증합니다. 클라우드 서버 생성 권한이 있는지는 판단하지 않으며, 권한 여부는 각 서비스의 정책 파일에 따라 결정됩니다\n1 2 3 4 5 6 7 8 9 [root@Controller nova]# cd /etc/openstack-dashboard/ [root@Controller openstack-dashboard]# ls cinder_policy.json keystone_policy.json neutron_policy.json nova_policy.json glance_policy.json local_settings nova_policy.d [root@Controller nova(keystone_user)]# cp /etc/openstack-dashboard/glance_policy.json /etc/glance/policy.json [root@Controller nova(keystone_user)]# openstack image create --container-format bare --disk-format qcow2 --file /root/cirros-0.3.5-x86_64-disk.img --public test2 [root@Controller nova(keystone_user)]# chgrp glance /etc/glance/policy.json [root@Controller nova(keystone_user)]# systemctl restart openstack-glance-api.service Nova 서비스 컨트롤러 노드\nnova-api 모듈\nnova-scheduler 모듈\nnova-conductor 모듈\nnova-novncproxy 모듈\n컴퓨트 노드\nnova-compute 모듈\nnova-api\n외부로 제공되는 REST 인터페이스 처리 담당\n전달된 파라미터의 유효성 검증과 제약 검사를 수행합니다\n요청된 리소스에 대해 쿼터(quota) 검증 및 예약을 수행합니다\n리소스 생성·수정·삭제·조회 등\n가상 머신 라이프사이클의 진입점\n수평 확장 형태로 배포 가능\nnova-conductor\nnova-compute의 데이터베이스 직접 접근을 분리합니다. 사용자가 클라우드 서버 생성을 요청해 성공하면 nova-conductor 모듈이 해당 정보를 데이터베이스에 기록합니다\ndatabase — 해당 컴포넌트 관련 정보(클라우드 서버 정보, 이미지 정보, 네트워크 정보 등)를 저장합니다\nnova-scheduler\nOpenStack 환경에 여러 컴퓨트 노드가 있을 때 사용자가 클라우드 서버를 생성하면 nova-scheduler 모듈이 각 compute 호스트의 가중치에 따라 어느 컴퓨트 노드에서 실행할지 결정합니다\nHAProxy가 로드 밸런싱을 제공합니다. 사용자가 nova-api에 접근하려 하면 요청은 먼저 HAProxy로 전달되고, 각 nova-api의 부하 상황에 따라 어떤 API 노드로 보낼지 결정됩니다\nMQ — Message Queue(메시지 큐)\n하나의 컴포넌트가 여러 모듈로 구성될 때, 모듈 간 통신은 메시지 큐를 거쳐 혼잡 제어 등을 수행합니다\n1000명의 사용자가 nova-api에 요청을 보낸 경우\nnova-api는 해당 요청을 nova-compute로 보냅니다\nnova-api는 이 1000개의 요청을 메시지 큐로 보내고, 메시지 큐는 각 compute 호스트의 메시지 소비 상황과 처리 진도에 따라 순서대로 nova-compute 노드에 전달해 처리합니다\nnova-compute\n실제로 가상 머신을 구동하는 모듈\nnova-novncproxy\n동작 원리: nova-compute 호스트는 실행 중인 가상 머신마다 VNC Server 포트를 하나 엽니다\nnova-compute 호스트는 해당 포트를 외부에 노출해서는 안 됩니다. 노출하면 인터넷상의 호스트가 그 포트와 가상 머신에 접근할 수 있게 됩니다\n이 문제를 해결하기 위해 컨트롤러 노드에서 nova-novncproxy 서비스를 띄워 외부에 제공합니다. 사용자가 해당 가상 머신 콘솔에 접근하면 요청이 먼저 nova-novncproxy로 전달되고, 제공된 정보(가상 머신 UUID, 토큰 등)로 그 가상 머신이 어느 nova-compute에서 실행 중인지 조회한 뒤 해당 Compute 호스트로 요청을 전달합니다.\nVNC로 가상 머신을 여는 동작 원리\nvncserver — 5900 포트\nvncviewer — vncviewer\n클라이언트에 vncviewer를 설치할 필요는 없으며, 해당 기능은 브라우저 HTML에 통합되어 있습니다(noVNC 프로토콜)\nGlance 및 Swift 서비스 I. 클라우드 서버 배포:\n사용자가 클라우드 서버 배포 명령을 보내 검증을 통과하면 nova-compute 호스트가 glance 이미지 서버에서 로컬(클라우드 서버는 로컬에 배포)로 이미지를 내려받습니다 — /var/lib/nova/instances/_base/\n클라우드 서버가 부팅되면 차분 디스크가 생성되며, 새로 쓰는 데이터는 그 차분 디스크에 기록됩니다\n실습: glance 이미지를 glance 로컬 디스크가 아닌 Swift 오브젝트 스토리지에 저장하기\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@Controller ~]# cd /etc/glance/ [root@Controller glance]# cp glance-api.conf glance-api.conf.bak [root@Controller ~]# vim /etc/glance/glance-api.conf 3057 stores=file,http,swift 3111 default_store=swift 3982 swift_store_region = RegionOne 4032 swift_store_endpoint_type = publicURL 4090 swift_store_container = public 4118 swift_store_large_object_size = 5120 4142 swift_store_large_object_chunk_size = 100 4160 swift_store_create_container_on_put = True 4182 swift_store_multi_tenant = True 4230 swift_store_admin_tenants = services 4382 swift_store_auth_version = 2 4391 swift_store_auth_address = http://192.168.100.10:5000/v3 4399 swift_store_user = zlm 4408 swift_store_key = 000000 [root@Controller ~]# systemctl restart openstack-glance-api.service 참고: zlm 사용자는 swiftOperator 역할을 부여받아야 Swift에 대한 작업 권한을 가집니다\n","permalink":"https://LMDAWN1127.github.io/ko/posts/openstack-keystone-glance-nova/","summary":"OpenStack 3대 핵심 서비스 정리: Keystone 다중 도메인 지원, service/endpoint 등록과 정책 파일 기반 권한 제어, Nova 모듈(api/scheduler/conductor/compute/novncproxy)과 메시지 큐·차분 디스크, 그리고 Swift 오브젝트 스토리지를 백엔드로 쓰는 Glance 이미지 서비스.","title":"OpenStack 핵심 서비스 상세 정리: Keystone 인증·Nova 컴퓨트·Glance/Swift 이미지 스토리지"},{"content":"I. GUI(대시보드)에서 클라우드 서버 제공 admin 사용자는 클라우드 최고 관리자입니다\nadmin은 아이덴티티를 관리합니다\n테넌트(프로젝트) 생성 사용자 생성 및 프로젝트 연결 이미지 업로드: 관리자가 업로드한 이미지는 퍼블릭(모든 테넌트 접근 가능)으로 설정할 수 있으며, 일반 사용자가 업로드한 이미지는 프라이빗으로 해당 테넌트 내에서만 공유됩니다. 플레이버 생성 외부 네트워크 생성 L3 Agent가 배포된 노드가 네트워크 노드가 됩니다\n테넌트로 로그인\n테넌트 계정으로 로그인 프라이빗 네트워크 및 서브넷(VPC) 생성 라우터 생성 및 외부/프라이빗 네트워크 연결 보안 그룹 및 규칙 생성 키 페어 생성 클라우드 서버 제공 및 플로팅 IP 연결 테스트(로그인, 호스트명, 네트워크) cloud-init이 호스트명·암호 등 사용자 정의 정보를 초기화합니다\n예를 들어 디스크 20GB를 차지하고 원활한 구동에 메모리 4GB 이상이 필요한 큰 이미지를 만들었다고 가정합니다. 하지만 테넌트는 이를 모르고 서버 제공 시 디스크 10GB·메모리 2GB 플레이버를 선택해 실패하며 원인도 모릅니다. 따라서 이미지 등록 시 최소 디스크 20GB·최소 메모리 4GB를 지정해 두면, 나중에 플레이버를 고를 때 해당 조건보다 작은 것은 선택할 수 없고 조건을 만족하는 것만 고를 수 있습니다.\n아래는 OpenStack Nova에서 새로 추가된 컴퓨트 노드를 소속 Cell 데이터베이스에 검색·매핑하기 위해 사용하는 핵심 명령입니다. Cells v2 아키텍처에서는 각 컴퓨트 노드가 반드시 이 등록 단계를 거쳐야 하며, 그렇지 않으면 스케줄러가 해당 노드에 인스턴스를 배치할 수 없고 API에도 해당 호스트가 목록에 나타나지 않습니다.\n1 [root@Controller ~(keystone_admin)]# nova-manage cell_v2 discover_hosts --verbose 클라우드 서버는 라우터를 통해 인터넷에 접근할 수 있습니다. 플로팅 IP를 구성하는 의미는 외부에 서비스를 노출하는 것입니다(웹 서버는 SNAT로 외부로 나갈 수 있지만 원격 사용자는 그 서비스에 들어올 수 없습니다). 아래는 OpenStack이 내부적으로 사용하는 iptables 규칙을 확인하는 방법입니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 [root@Controller ~(keystone_admin)]# ip netns qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 (id: 1) qdhcp-a82a6d9e-26ec-497f-8f71-68d8395386ad (id: 0) [root@Controller ~(keystone_admin)]# ip netns exec qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 ip addr list 1: lo: \u0026lt;LOOPBACK,UP,LOWER_UP\u0026gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 33: qg-d9305000-ef: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether fa:16:3e:18:37:69 brd ff:ff:ff:ff:ff:ff inet 192.168.10.106/24 brd 192.168.10.255 scope global qg-d9305000-ef valid_lft forever preferred_lft forever inet 192.168.10.182/32 brd 192.168.10.182 scope global qg-d9305000-ef valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe18:3769/64 scope link valid_lft forever preferred_lft forever 34: qr-eb02e78e-a3: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether fa:16:3e:18:b8:5f brd ff:ff:ff:ff:ff:ff inet 192.168.20.254/24 brd 192.168.20.255 scope global qr-eb02e78e-a3 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe18:b85f/64 scope link valid_lft forever preferred_lft forever [root@Controller ~(keystone_admin)]# ip netns exec qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 iptables -t nat -nL Chain PREROUTING (policy ACCEPT) target prot opt source destination neutron-l3-agent-PREROUTING all -- 0.0.0.0/0 0.0.0.0/0 Chain INPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination neutron-l3-agent-POSTROUTING all -- 0.0.0.0/0 0.0.0.0/0 neutron-postrouting-bottom all -- 0.0.0.0/0 0.0.0.0/0 Chain OUTPUT (policy ACCEPT) target prot opt source destination neutron-l3-agent-OUTPUT all -- 0.0.0.0/0 0.0.0.0/0 Chain neutron-l3-agent-OUTPUT (1 references) target prot opt source destination DNAT all -- 0.0.0.0/0 192.168.10.182 to:192.168.20.197 Chain neutron-l3-agent-POSTROUTING (1 references) target prot opt source destination ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ! ctstate DNAT Chain neutron-l3-agent-PREROUTING (1 references) target prot opt source destination REDIRECT tcp -- 0.0.0.0/0 169.254.169.254 tcp dpt:80 redir ports 9697 DNAT all -- 0.0.0.0/0 192.168.10.182 to:192.168.20.197 Chain neutron-l3-agent-float-snat (1 references) target prot opt source destination SNAT all -- 192.168.20.197 0.0.0.0/0 to:192.168.10.182 random-fully Chain neutron-l3-agent-snat (1 references) target prot opt source destination neutron-l3-agent-float-snat all -- 0.0.0.0/0 0.0.0.0/0 SNAT all -- 0.0.0.0/0 0.0.0.0/0 to:192.168.10.106 random-fully SNAT all -- 0.0.0.0/0 0.0.0.0/0 mark match ! 0x2/0xffff ctstate DNAT to:192.168.10.106 random-fully Chain neutron-postrouting-bottom (1 references) target prot opt source destination neutron-l3-agent-snat all -- 0.0.0.0/0 0.0.0.0/0 /* Perform source NAT on outgoing traffic. */ 실습 과제: 인터넷에 접근 가능한 클라우드 서버를 생성하고, yum 저장소를 Alibaba Cloud로 지정한 뒤 httpd 패키지를 설치·서비스를 시작하고, 외부 호스트에서 Web 페이지 http://192.168.10.182 에 접근한다.\nCompute 호스트는 본질적으로 KVM 하이퍼바이저입니다. OpenStack이 클라우드 서버를 제공할 때 스케줄러가 그 중 한 Compute 호스트에 배치합니다.\nII. 명령줄(CLI)에서 클라우드 서버 제공 1 [root@Controller ~(keystone_admin)]# cp keystonerc_admin keystonerc_zlm 요구사항 1: CLI로 모든 리소스 삭제\n1 2 3 4 5 6 7 8 9 10 11 12 [root@Controller ~(keystone_zlm)]# cat keystonerc_zlm unset OS_SERVICE_TOKEN export OS_USERNAME=zlm export OS_PASSWORD=\u0026#39;000000\u0026#39; export OS_REGION_NAME=RegionOne export OS_AUTH_URL=http://192.168.100.10:5000/v3 export PS1=\u0026#39;[\\u@\\h \\W(keystone_zlm)]\\$ \u0026#39; export OS_PROJECT_NAME=LMandMY_HOME export OS_USER_DOMAIN_NAME=Default export OS_PROJECT_DOMAIN_NAME=Default export OS_IDENTITY_API_VERSION=3 [root@Controller ~(keystone_zlm)]# source keystonerc_zlm 클라우드 서버 삭제 1 2 3 4 5 6 7 [root@Controller ~(keystone_zlm)]# openstack server list +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ | ID | Name | Status | Networks | Image | Flavor | +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ | 44f0fa66-94ed-44ac-a9ae-0a3a13c1faba | web | ACTIVE | VPC01=192.168.20.197, 192.168.10.182 | CentOS 8.4 64bit | m3.web | +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ [root@Controller ~(keystone_zlm)]# openstack server delete 44f0fa66-94ed-44ac-a9ae-0a3a13c1faba 볼륨 삭제 1 2 [root@Controller ~(keystone_zlm)]# openstack volume list [root@Controller ~(keystone_zlm)]# openstack volume delete data01 라우터 삭제 1 2 3 4 5 [root@Controller ~(keystone_zlm)]# openstack router list [root@Controller ~(keystone_zlm)]# openstack router show router01 [root@Controller ~(keystone_zlm)]# openstack subnet list [root@Controller ~(keystone_zlm)]# openstack router remove subnet router01 subnet01 [root@Controller ~(keystone_zlm)]# openstack router delete router01 보안 그룹 삭제 1 2 [root@Controller ~(keystone_zlm)]# openstack security group list [root@Controller ~(keystone_zlm)]# openstack security group delete SG01 네트워크 삭제 1 2 [root@Controller ~(keystone_zlm)]# openstack network list [root@Controller ~(keystone_zlm)]# openstack network delete VPC01 admin 계정으로 전환하여 퍼블릭 네트워크·이미지·플레이버·테넌트·사용자 등을 삭제합니다.\n1 [root@Controller ~(keystone_zlm)]# source keystonerc_admin 퍼블릭 네트워크 삭제 1 2 [root@Controller ~(keystone_admin)]# openstack network list [root@Controller ~(keystone_admin)]# openstack network delete public 이미지 삭제 1 2 3 4 [root@Controller ~(keystone_admin)]# openstack image list [root@Controller ~(keystone_admin)]# openstack image delete e3336d90-e673-4b1b-82ec-69b70fd6402c [root@Controller ~(keystone_admin)]# openstack image delete cec813dd-8141-4a99-8883-805ac6e3ec17 [root@Controller ~(keystone_admin)]# openstack image delete c6ffe7db-ccb0-4df4-8715-e8ce0fd5dc7d 플레이버 삭제 1 2 [root@Controller ~(keystone_admin)]# openstack flavor list [root@Controller ~(keystone_admin)]# openstack flavor delete m3.web 테넌트 및 사용자 삭제 1 2 3 4 [root@Controller ~(keystone_admin)]# openstack project list [root@Controller ~(keystone_admin)]# openstack project delete LMandMY_HOME [root@Controller ~(keystone_admin)]# openstack user list [root@Controller ~(keystone_admin)]# openstack user delete zlm 요구사항 2: CLI로 모든 리소스 생성—서버 제공, 플로팅 IP 추가, 서버 접속, 네트워크 생성\n1 [root@Controller ~(keystone_admin)]# source keystonerc_admin 프로젝트 사용자를 생성하고 연결 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@Controller ~(keystone_admin)]# openstack project create LMandMY_HOME [root@Controller ~(keystone_admin)]# openstack user create --project LMandMY_HOME --project-domain Default --password-prompt zlm [root@Controller ~(keystone_admin)]# openstack role list +----------------------------------+------------------+ | ID | Name | +----------------------------------+------------------+ | 0adc17230a5a434195d5995863280b3d | heat_stack_owner | | 2e5abdde67bb4f23bbf15454616af53e | ResellerAdmin | | 46d49fecd6f4411bbd72d0b9f575bcae | admin | | 5f5bf78c40b143bbb2df74fc855ea204 | reader | | 84ebb81407f644b18e1a529e0c317f16 | member | | b83f4c27c83445489554e988fb9af5a1 | heat_stack_user | | b8f8cd45891240bfa0f80e93f0e37ab9 | _member_ | | ebc22a15458c4712bf0dd4f4de09e0bf | SwiftOperator | +----------------------------------+------------------+ [root@Controller ~(keystone_admin)]# openstack role add --user zlm --project LMandMY_HOME b8f8cd45891240bfa0f80e93f0e37ab9 이미지 업로드 1 [root@Controller ~(keystone_admin)]# openstack image create --file /data/cirros-0.3.5-x86_64-disk.img --disk-format qcow2 --container-format bare --public cirros 플레이버 생성 1 [root@Controller ~(keystone_admin)]# openstack flavor create --ram 1024 --disk 10 --public m3.web 퍼블릭 네트워크 및 서브넷 생성 1 [root@Controller ~(keystone_admin)]# openstack network create --external --share --provider-network-type flat --provider-physical-network extnet --project LMandMY_HOME public 서브넷 생성\n1 [root@Controller ~(keystone_admin)]# openstack subnet create --no-dhcp --subnet-range 192.168.10.0/24 --allocation-pool start=192.168.10.100,end=192.168.10.200 --gateway 192.168.10.1 --network public publicsub01 테넌트 계정으로 전환\n1 [root@Controller ~(keystone_admin)]# source keystonerc_zlm 네트워크 및 서브넷 생성 1 [root@Controller ~(keystone_zlm)]# openstack network create VPC01 서브넷 생성\n1 [root@Controller ~(keystone_zlm)]# openstack subnet create --network VPC01 --subnet-range 192.168.20.0/24 --dhcp --gateway 192.168.20.254 --dns-nameserver 114.114.114.114 subnet01 라우터 생성 1 2 3 [root@Controller ~(keystone_zlm)]# openstack router create router01 [root@Controller ~(keystone_zlm)]# openstack router add subnet router01 subnet01 [root@Controller ~(keystone_zlm)]# openstack router set --external-gateway public router01 보안 그룹 생성 1 [root@Controller ~(keystone_zlm)]# openstack security group create SG01 ICMP 프로토콜 허용\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol icmp --remote-ip 0.0.0.0/0 SG01 SSH 서비스 허용\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol tcp --dst-port 22 --remote-ip 0.0.0.0/0 SG01 포트 80 허용\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol tcp --dst-port 80 --remote-ip 0.0.0.0/0 SG01 플로팅 IP 생성 1 [root@Controller ~(keystone_zlm)]# openstack floating ip create public 키 페어 생성 1 [root@Controller ~(keystone_zlm)]# openstack key create key01 \u0026gt; key01.pem 클라우드 서버 생성 1 2 3 4 5 6 7 8 [root@Controller ~(keystone_zlm)]# openstack network list +--------------------------------------+--------+--------------------------------------+ | ID | Name | Subnets | +--------------------------------------+--------+--------------------------------------+ | a82a6d9e-26ec-497f-8f71-68d8395386ad | VPC01 | 40bd75e0-3d22-4e7b-8659-8a6c0a44e3aa | | d99ba601-05e3-4f1f-88b8-f0587c6a5f6e | public | 5d39db23-8359-4e33-bde7-0f9ac6a0f7c7 | +--------------------------------------+--------+--------------------------------------+ [root@Controller ~(keystone_zlm)]# openstack server create --image cirros --security-group SG01 --key-name key01 --flavor m3.web --nic net-id=a82a6d9e-26ec-497f-8f71-68d8395386ad web 플로팅 IP를 클라우드 서버에 연결\n1 2 [root@Controller ~(keystone_zlm)]# openstack port list [root@Controller ~(keystone_zlm)]# openstack floating ip set --port=46ba21d5-6f14-4693-a215-5a6fc8781adf 192.168.10.182 클라우드 서버 접속\n1 2 [root@Controller ~(keystone_zlm)]# chmod 400 key01.pem [root@Controller ~(keystone_zlm)]# ssh -i key01.pem cirros@192.168.10.182 외부 NIC 통신이 안 되는 경우\n1 [root@Controller ~(keystone_zlm)]# ifup br-ex ; ifup ens224 ","permalink":"https://LMDAWN1127.github.io/ko/posts/openstack-provision-cloud-server/","summary":"GUI부터 CLI까지 OpenStack 클라우드 서버 제공 전체 흐름을 완전 정리: 테넌트·사용자·이미지·플레이버·네트워크·라우터·보안 그룹·키 페어·플로팅 IP 생성 및 삭제, 그리고 iptables SNAT/DNAT와 Nova cell_v2 discover_hosts 트러블슈팅.","title":"OpenStack 클라우드 서버 프로비저닝"},{"content":"01 - systemctl 데이터베이스 Service 파일 정리 방안 1: 표준 운영 데몬 구성 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [Unit] Description=MySQL Server After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid ExecReload=/bin/kill -HUP $MAINPID PIDFile=/var/run/mysqld/mysqld.pid PrivateTmp=true RuntimeDirectory=mysqld [Install] WantedBy=multi-user.target 방안 2: 설정 파일 및 파일 핸들 제한 포함 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Unit] Description=MySQL Server After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf RuntimeDirectory=mysqld LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target 수정 완료 후\n1 2 3 [root@master local]#systemctl daemon-reload [root@master local]#systemctl restart mysqld.service [root@master local]#systemctl enable mysqld.service 02 - 멀티버전 MySQL (5.6 / 5.7) 설치 및 업그레이드 준비 실전 1단계: 소프트웨어 아카이브 다운로드 1 2 3 4 5 # MySQL 5.7.40 바이너리 패키지 [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.40-linux-glibc2.12-x86_64.tar.gz # MySQL 5.6.48 바이너리 패키지 [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.48-linux-glibc2.12-x86_64.tar.gz 2단계: 압축 해제, 설치 및 심볼릭 링크 생성 1 2 3 4 5 6 7 8 9 # 5.7 버전 설치 [root@master local]#tar xf mysql-5.7.40-linux-glibc2.12-x86_64.tar.gz [root@master local]#ln -s mysql-5.7.40-linux-glibc2.12-x86_64 mysql57 # 참고: 멀티버전 공존 디버깅 기간에는 전역 환경변수를 아직 설정하지 말고 명령 경로 충돌 방지 # 5.6 버전 설치 [root@master local]#tar xf mysql-5.6.48-linux-glibc2.12-x86_64.tar.gz [root@master local]#ln -s mysql-5.6.48-linux-glibc2.12-x86_64 mysql56 # 참고: 당분간 전역 환경변수 설정하지 않음 3단계: 데이터베이스 소프트웨어 초기화 MySQL 5.7 초기화 (데이터 디렉터리 3357):\n1 2 3 4 5 [root@master local]#mkdir -p /data/3357/data [root@master local]#chown -R mysql.mysql /data/3357/data [root@master local]#/usr/local/mysql57/bin/mysqld --initialize-insecure --user=mysql --datadir=/data/3357/data --basedir=/usr/local/mysql57 [root@master local]#echo $? 0 MySQL 5.6 초기화 (데이터 디렉터리 3356):\n1 2 3 4 5 6 7 8 9 10 11 [root@master local]#mkdir -p /data/3356/data [root@master local]#chown -R mysql.mysql /data/3356/data [root@master local]#/usr/local/mysql56/scripts/mysql_install_db --user=mysql --datadir=/data/3356/data --basedir=/usr/local/mysql56 FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql56/scripts/mysql_install_db: Data::Dumper # Perl 모듈 누락 [root@master local]#yum install -y perl-Data-Dumper [root@master local]#/usr/local/mysql56/scripts/mysql_install_db --user=mysql --datadir=/data/3356/data --basedir=/usr/local/mysql56 # 출력에 OK 두 개가 포함되는지 확인하고 종료 코드를 확인: [root@master local]#echo $? 0 4단계: 다중 인스턴스 전용 설정 파일 작성 MySQL 5.7 (인스턴스 3357) 설정:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@master local]#vim /etc/my3357.cnf [mysql] socket=/tmp/mysql3357.sock [mysqld] user=mysql basedir=/usr/local/mysql57 datadir=/data/3357/data socket=/tmp/mysql3357.sock port=3357 pid-file=/run/mysqld3357/mysqld.pid [root@master local]#vim /usr/lib/systemd/system/mysqld3357.service [Unit] Description=MySQL 5.7 Server (3357) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql57/bin/mysqld --defaults-file=/etc/my3357.cnf RuntimeDirectory=mysqld3357 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target MySQL 5.6 (인스턴스 3356) 설정:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@master local]#vim /etc/my3356.cnf [mysql] socket=/tmp/mysql3356.sock [mysqld] user=mysql basedir=/usr/local/mysql56 datadir=/data/3356/data socket=/tmp/mysql3356.sock port=3356 pid-file=/run/mysqld3356/mysqld.pid [root@master local]#vim /usr/lib/systemd/system/mysqld3356.service [Unit] Description=MySQL 5.6 Server (3356) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql56/bin/mysqld --defaults-file=/etc/my3356.cnf RuntimeDirectory=mysqld3356 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target 5단계: 실행, 시작 및 관리 검증 MySQL 5.7 서비스 시작/중지:\n1 2 3 4 5 6 7 8 9 10 11 # 방식 1: 시작 [root@master local]#cp /usr/local/mysql57/support-files/mysql.server /etc/init.d/mysqld57 [root@master local]#chmod +x /etc/init.d/mysqld57 [root@master local]#/etc/init.d/mysqld57 start [root@master local]#/etc/init.d/mysqld57 stop # 방식 2: 시작 [root@master local]#systemctl daemon-reload [root@master local]#systemctl start mysqld3357.service [root@master local]#systemctl enable mysqld3357.service Created symlink from /etc/systemd/system/multi-user.target.wants/mysqld3357.service to /usr/lib/systemd/system/mysqld3357.service. MySQL 5.6 서비스 시작/중지:\n1 2 3 4 5 6 7 8 9 10 11 # 방식 1: 시작 [root@master local]#cp /usr/local/mysql56/support-files/mysql.server /etc/init.d/mysqld56 [root@master local]#chmod +x /etc/init.d/mysqld56 [root@master local]#/etc/init.d/mysqld56 start [root@master local]#/etc/init.d/mysqld56 stop # 방식 2: 시작 [root@master local]#systemctl daemon-reload [root@master local]#systemctl start mysqld3356.service [root@master local]#systemctl enable mysqld3356.service Created symlink from /etc/systemd/system/multi-user.target.wants/mysqld3356.service to /usr/lib/systemd/system/mysqld3356.service. 세 서비스 상태 검증 (모두 running)\n1 2 3 4 5 6 7 8 [root@master local]#for svc in mysqld mysqld3356 mysqld3357; do \u0026gt; a=$(systemctl show -p ActiveState \u0026#34;$svc\u0026#34; | cut -d= -f2) \u0026gt; s=$(systemctl show -p SubState \u0026#34;$svc\u0026#34; | cut -d= -f2) \u0026gt; echo \u0026#34;$svc: $a ($s)\u0026#34; \u0026gt; done mysqld: active (running) mysqld3356: active (running) mysqld3357: active (running) ","permalink":"https://LMDAWN1127.github.io/ko/posts/systemd-mysql-multiversion/","summary":"systemd Unit 관리와 MySQL 5.6/5.7 멀티버전·다중 인스턴스 배포 실전: 두 가지 운영 데몬 구성, 아카이브 다운로드, 초기화, 인스턴스별 설정 파일 작성, 그리고 시작/중지 및 3개 서비스 상태 검증.","title":"systemd 서비스 관리와 MySQL 멀티버전 배포"},{"content":"01 - 데이터베이스 서비스 설치 및 배포 1.1 데이터베이스 서버 패키지 다운로드 공식 다운로드 주소 (역사 보관 바이너리 설치 패키지):\n1 2 3 # 저장 디렉터리 [root@master ~]#cd /usr/local/ [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz 설치 패키지 정보 확인\n1 2 [root@master local]#ll mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz -rw-r--r-- 1 root root 914806904 Jul 2 2021 mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz 1.2 바이너리 패키지로 데이터베이스 서버 설치 1단계: 운영체제 환경 준비 1. 네트워크 주소 확인 1 2 3 4 5 6 7 [root@master local]#ip addr show eth0 2: eth0: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:0c:29:4b:b7:38 brd ff:ff:ff:ff:ff:ff inet 11.0.1.101/24 brd 11.0.1.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::20c:29ff:fe4b:b738/64 scope link valid_lft forever preferred_lft forever 2. 호스트명 해석 설정 1 2 3 4 [root@master local]#vim /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 11.0.1.101 master 3. 시스템 방화벽 비활성화 1 [root@master local]#systemctl disable firewalld --now 4. SELinux 비활성화 1 2 3 4 5 [root@master local]#sed -i \u0026#39;s/^SELINUX=enforcing/SELINUX=disabled/\u0026#39; /etc/selinux/config # 재부팅 적용 [root@master local]#reboot [root@master local]#getenforce Disabled 5. 데이터베이스 설치 시 충돌 방지 1 2 3 4 5 6 # 시스템에 기본 포함된 mariadb가 있는지 확인 [root@master local]#rpm -qa | grep mariadb mariadb-libs-5.5.68-1.el7.x86_64 # 시스템 기본 데이터베이스 패키지 제거 [root@master local]#yum remove -y mariadb-libs 6. 데이터베이스 의존 소프트웨어 설치 (누락 시 초기화 오류) 1 [root@master local]#yum install -y libaio-devel 2단계: 데이터베이스 소프트웨어 설치 1. 패키지 압축 해제 및 심볼릭 링크 생성 (버전 관리 용이) 1 2 [root@master local]#tar xf mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz [root@master local]#ln -s mysql-8.0.26-linux-glibc2.12-x86_64 mysql 2. 시스템 전역 환경변수 설정 1 2 3 4 5 [root@master local]#vim /etc/profile # 파일 끝에 추가 export PATH=$PATH:/usr/local/mysql/bin # 설정 적용 [root@master local]#source /etc/profile 3. 설치 버전 확인 1 2 [root@master local]#mysql -V mysql Ver 8.0.26 for Linux on x86_64 (MySQL Community Server - GPL) 3단계: 소프트웨어 초기화 MySQL 의 핵심 디렉터리 두 개:\n프로그램 디렉터리: /usr/local/mysql 데이터 디렉터리: /data/3306/data (실제 데이터 저장) 1. 전용 실행 사용자 생성 (시스템 로그인 불필요) 1 [root@master local]#useradd mysql -M -s /sbin/nologin 2. 데이터 저장 디렉터리 생성 및 권한 부여 1 2 [root@master local]#mkdir /data/3306/data -p [root@master local]#chown mysql.mysql /data/3306/data 3. 데이터 디렉터리 초기화 실행 1 2 3 4 5 # 존재할 수 있는 간섭 설정 파일 제거 [root@master local]#rm -f /etc/my.cnf # 비밀번호 없이 초기화 실행 (초기화 후 관리자 비밀번호 없음) [root@master local]#mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 4단계: 데이터베이스 서비스 설정 파일 작성 /etc/my.cnf 생성 및 편집:\n1 2 3 4 5 6 7 8 9 10 [root@master local]#cat \u0026gt; /etc/my.cnf \u0026lt;\u0026lt; \u0026#39;eof\u0026#39; \u0026gt; [mysql] \u0026gt; socket=/tmp/mysql.sock \u0026gt; \u0026gt; [mysqld] \u0026gt; user=mysql \u0026gt; basedir=/usr/local/mysql \u0026gt; datadir=/data/3306/data \u0026gt; socket=/tmp/mysql.sock \u0026gt; eof 참고: 설정 파일의 basedir 및 datadir 경로는 실제 환경에 맞게 조정해야 함.\n5단계: 데이터베이스 서비스 시작 1. 공식 서비스 관리 스크립트로 시작 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@master local]#cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld [root@master local]#/etc/init.d/mysqld start Starting MySQL.Logging to \u0026#39;/data/3306/data/master.err\u0026#39;. . SUCCESS! # 정상적으로 데이터베이스에 접속하면 시작 성공 [root@master local]#mysql -uroot Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; 2. 명령줄에서 직접 시작 mysqld 명령으로 시작: 1 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/data/3306/data --user=mysql --socket=/tmp/mysql.sock \u0026amp; mysqld_safe 안전 데몬으로 시작: 1 /usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data \u0026amp; 3. systemctl / service 로 시작 방법 1: 전용 Systemd Unit 파일 작성 (/etc/systemd/system/mysqld.service) 방법 2: /etc/init.d/ 디렉터리의 서비스 스크립트를 직접 활성화 및 호출: 1 2 3 4 systemctl enable mysqld service mysqld start # 또는 systemctl start mysqld 1.3 보충 지식 1. 초기화 방법 두 가지 비교 방법 1: 비보안 초기화 (--initialize-insecure)\n1 mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 특징: DB 관리자에게 비밀번호가 없어 바로 비밀번호 없이 로그인 가능하며, 테스트나 스크립트 초기화에 편리. 방법 2: 보안 초기화 (--initialize)\n1 mysqld --initialize --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 특징: DB 관리자에게 무작위 임시 비밀번호가 생성되어 로그에 출력되며, 최초 로그인에 사용. 주의: 임시 비밀번호는 최초 로그인 전용이며, 로그인 후 반드시 새 비밀번호로 재설정해야 하며 변경 전에는 다른 작업을 실행할 수 없음: 1 ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; 2. 데이터베이스 시작 실패 표준 해결 절차 1단계: 시스템 기본 환경 확인 SELinux 가 완전히 비활성화되었는지 확인; 포트 3306 이 점유되지 않았는지, 다른 충돌하는 데이터베이스 서비스 프로세스가 있는지 확인. 2단계: 오류 로그 조사 및 분석 전형적인 오류: Starting MySQL. ERROR! ... PID file ... 오류 로그 상세 확인: 1 2 3 cat /data/3306/data/master.err # 로그 오류 예시: # 2024-06-14T09:15:36.120048Z 1 [ERROR] [MY-012271] [InnoDB] The innodb_system data file \u0026#39;ibdata1\u0026#39; must be writable 3단계: 오류 로그가 없을 때 조사 방법 설정 파일 /etc/my.cnf 확인: 설정 파일 경로와 파일 이름이 올바른지 확인; 설정 파일 내부 파라미터 구문 및 경로 설정이 올바른지 확인. 02 - 데이터베이스 서비스 시작 및 중지 데이터베이스 서비스를 시작하고 중지하는 방법은 여러 가지이며, 일반적인 방법을 아래에 정리함:\n번호 시작 방법 해당 중지 방법 설명 및 권장 01 mysqld \u0026amp; kill / pkill / killall 비권장; kill -9 사용 엄금 02 mysqld_safe \u0026amp; kill / pkill / killall 직접 kill 비권장 03 mysql.server start mysql.server stop 공식 기본 제어 스크립트 04 service mysqld start service mysqld stop 권장 (고전 서비스 관리) 05 systemctl start mysqld systemctl stop mysqld 권장 (현대 Linux 표준) 06 클라이언트/관리 명령 mysqladmin -uroot -p비밀번호 shutdown 관리자가 원격 또는 로컬 명령줄에서 정상 종료 07 SQL 세션 내 종료 터미널에서 shutdown; 실행 관리자 권한 필요 03 - 데이터베이스 서비스 비밀번호 관리 1. 비밀번호가 없을 때 설정 방법 1: 명령줄 이용\n1 2 3 [root@master local]#mysqladmin -uroot password \u0026#39;123456\u0026#39; mysqladmin: [Warning] Using a password on the command line interface can be insecure. Warning: Since password will be sent to server in plain text, use ssl connection to ensure password safety. 방법 2: SQL 문 이용\n1 2 mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;000000\u0026#39;; Query OK, 0 rows affected (0.02 sec) 2. 기존 비밀번호 변경 방법 1: 명령줄 이용 1 2 3 [root@master local]#mysqladmin -uroot -p000000 password \u0026#39;123456\u0026#39; mysqladmin: [Warning] Using a password on the command line interface can be insecure. Warning: Since password will be sent to server in plain text, use ssl connection to ensure password safety. 방법 2: SQL 문 이용 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@master local]#mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 14 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; 3. 비밀번호를 잊어버린 경우 (비밀번호 재설정) 비밀번호 재설정 실전 절차 1단계: 데이터베이스 서비스 중지\n1 mysql\u0026gt; shutdown; 또는 시스템 터미널에서 실행:\n1 2 3 4 [root@master local]#service mysqld stop Shutting down MySQL.. SUCCESS! # 또는 pkill mysqld 2단계: 안전 모드로 시작 (권한 테이블 로드 건너뛰기로 비밀번호 없이 로그인)\n1 2 [root@master local]#/usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data --skip-grant-tables --skip-networking \u0026amp; [1] 2658 설명: --skip-networking 은 네트워크 수신을 비활성화하여 비밀번호 없는 동안 다른 네트워크 클라이언트의 개입을 방지함.\n3단계: 비밀번호 없이 로그인 후 재설정\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@master local]#mysql Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 7 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; FLUSH PRIVILEGES; Query OK, 0 rows affected (0.01 sec) mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; MySQL 터미널에서 순서대로 실행:\n1 2 3 4 5 6 7 -- 1. 권한 테이블 리플러시 (메모리와 디스크의 권한 테이블 동기화) -- 작용1: 메모리의 권한 테이블 정보를 디스크에 동기화 기록 -- 작용2: 디스크의 권한 테이블 정보를 메모리로 재로드 FLUSH PRIVILEGES; -- 2. 비밀번호 재설정 ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; 주의: FLUSH PRIVILEGES; 를 실행하지 않고 ALTER USER 를 바로 실행하면 시스템이 오류를 발생:\n1 ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement 4단계: 데이터베이스 서비스 정상 재시작\n1 2 3 4 5 6 7 # 임시 안전 모드 프로세스 종료 [root@master local]#pkill mysqld # 데이터베이스 서비스 정상 시작 [root@master local]#/etc/init.d/mysqld start Starting MySQL.. SUCCESS! # 또는 service mysqld start 5단계: 새 비밀번호로 로그인 검증\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@master local]#mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; ","permalink":"https://LMDAWN1127.github.io/ko/posts/mysql-binary-deploy/","summary":"MySQL 8.0 운영 실전: 공식 바이너리 패키지 설치, OS 준비, 초기화, 설정 파일, 다양한 시작/중지 방법, 그리고 비밀번호 설정/변경/분실 시 재설정.","title":"MySQL 실전: 바이너리 설치, 시작/중지, 비밀번호 관리"},{"content":"01. SonarQube 소개 1 2 3 1. SonarQube 은 Java 로 개발되었으며 Open JDK 8 이 필요 2. SonarQube 은 MySQL 데이터베이스에 의존하며 최소 5.6 이상 3. SonarQube 소규모 인스턴스는 최소 4G, 대규모 인스턴스는 16G 메모리 필요 02. 배포 1. JDK 실행 환경 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@sonarqube ~]#ll total 166040 -rw-r--r-- 1 root root 170023183 Oct 1 22:32 jdk-8u181-linux-x64.rpm [root@sonarqube ~]#rpm -ivh jdk-8u181-linux-x64.rpm warning: jdk-8u181-linux-x64.rpm: Header V3 RSA/SHA256 Signature, key ID ec551f03: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jdk1.8-2000:1.8.0_181-fcs ################################# [100%] Unpacking JAR files... tools.jar... plugin.jar... javaws.jar... deploy.jar... rt.jar... jsse.jar... charsets.jar... localedata.jar... 2. 데이터베이스 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 # 1. MySQL 공식 Yum 저장소 설정 파일 다운로드 [root@sonarqube ~]#wget dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm # 2. MySQL 저장소 설치 [root@sonarqube ~]#rpm -ivh mysql-community-release-el6-5.noarch.rpm Preparing... ################################# [100%] Updating / installing... 1:mysql-community-release-el6-5 ################################# [100%] # 3. MySQL 서버 설치 [root@sonarqube ~]# yum install mysql-community-server -y [root@sonarqube ~]#mysql --version mysql Ver 14.14 Distrib 5.6.51, for Linux (x86_64) using EditLine wrapper # 4. MySQL 서비스 시작 [root@sonarqube ~]#service mysqld start Starting mysqld (via systemctl): [ OK ] [root@sonarqube ~]#netstat -ntpl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1227/master tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1008/sshd tcp6 0 0 ::1:25 :::* LISTEN 1227/master tcp6 0 0 :::3306 :::* LISTEN 3113/mysqld tcp6 0 0 :::22 :::* LISTEN 1008/sshd # 5. 비밀번호 설정 [root@sonarqube ~]# mysqladmin -uroot password 000000 # 6. sonarqube 데이터베이스 생성 [root@sonarqube ~]# mysql -uroot -p000000 -e \u0026#34;CREATE DATABASE sonarqube DEFAULT CHARACTER SET utf8;\u0026#34; [root@sonarqube ~]#mysql -u root -p000000 -e \u0026#34;show databases\u0026#34; Warning: Using a password on the command line interface can be insecure. +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sonarqube | +--------------------+ 3. SonarQube 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 # 1. SonarQube 패키지 업로드 [root@sonarqube ~]# cd /usr/local/ [root@sonarqube local]#ll sonarqube-7.0.zip -rw-r--r-- 1 root root 155709573 Oct 1 22:50 sonarqube-7.0.zip # 2. 현재 디렉터리에 압축 해제 [root@sonarqube local]#yum install -y unzip [root@sonarqube local]# unzip sonarqube-7.0.zip [root@sonarqube local]# ln -s sonarqube-7.0 sonarqube # 3. 플러그인 설치: 기본 플러그인 삭제: [root@sonarqube ~]# rm -rf /usr/local/sonarqube/extensions/plugins/* 플러그인 업로드: [root@sonarqube ~]#ll sonar_plugins.tar.gz -rw-r--r-- 1 root root 45106788 Oct 1 22:56 sonar_plugins.tar.gz [root@sonarqube ~]#tar xf sonar_plugins.tar.gz -C /usr/local/sonarqube/extensions/plugins/ [root@sonarqube ~]#ll /usr/local/sonarqube/extensions/plugins/ total 4 drwxr-xr-x 2 1000 1000 4096 Oct 24 2019 plugins [root@sonarqube ~]# cd /usr/local/sonarqube/extensions/plugins/ [root@sonarqube plugins]# mv plugins/* . [root@sonarqube plugins]#rm -fr plugins/ # 4. 일반 사용자 생성 [root@sonarqube ~]# useradd sonarqube # 5. 코드 소유자를 sonarqube 로 변경 [root@sonarqube ~]# chown -R sonarqube.sonarqube /usr/local/sonarqube/ [root@sonarqube ~]# ll /usr/local/sonarqube/ total 12 drwxr-xr-x 8 sonarqube sonarqube 136 Feb 2 2018 bin drwxr-xr-x 2 sonarqube sonarqube 50 May 15 15:09 conf -rw-r--r-- 1 sonarqube sonarqube 7651 Feb 2 2018 COPYING drwxr-xr-x 2 sonarqube sonarqube 24 Feb 2 2018 data drwxr-xr-x 7 sonarqube sonarqube 150 Feb 2 2018 elasticsearch drwxr-xr-x 4 sonarqube sonarqube 40 Feb 2 2018 extensions drwxr-xr-x 9 sonarqube sonarqube 138 Feb 2 2018 lib drwxr-xr-x 2 sonarqube sonarqube 6 Feb 2 2018 logs drwxr-xr-x 2 sonarqube sonarqube 24 Feb 2 2018 temp drwxr-xr-x 9 sonarqube sonarqube 4096 Feb 2 2018 web # 6. 데이터베이스 연결 설정 [root@sonarqube ~]#vim /usr/local/sonarqube/conf/sonar.properties ... sonar.jdbc.username=root # 데이터베이스 사용자 이름 sonar.jdbc.password=000000 # 데이터베이스 비밀번호 #----- Embedded Database (default) # H2 embedded database server listening port, defaults to 9092 #sonarqube.embeddedDatabase.port=9092 #----- MySQL 5.6 or greater # Only InnoDB storage engine is supported (not myISAM). # Only the bundled driver is supported. It can not be changed. sonar.jdbc.url=jdbc:mysql://localhost:3306/sonarqube?useUnicode=true\u0026amp;characterE ncoding=utf8\u0026amp;rewriteBatchedStatements=true\u0026amp;useConfigs=maxPerformance\u0026amp;useSSL =false # 로컬 연결 활성화 ... 4. SonarQube 실행 1 2 SonarQube 은 root 로 시작할 수 없으며 반드시 일반 사용자로 실행해야 함 [root@sonarqube ~]# su - sonarqube -c \u0026#34;/usr/local/sonarqube/bin/linux-x86-64/sonar.sh start\u0026#34; 5. 원격 접속 토큰 설정 시작 후 포트는 9000 이며, 브라우저에서 http://11.0.1.202:9000/about 에 접속해 admin / admin 으로 로그인하고 토큰을 생성한다（한 번만 표시되므로 기록해 둘 것).\n1 2 3 4 5 [root@jenkins ~]# sonar-scanner \\ -Dsonar.projectKey=html \\ -Dsonar.sources=. \\ -Dsonar.host.url=http://11.0.1.202:9000 \\ -Dsonar.login=a3bd8e829343edabbbc58fefe0d3e97fdb12268b 6. 클라이언트 배포 클라이언트 IP: 11.0.1.201\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # 1. 클라이언트 업로드 [root@jenkins ~]# cd /usr/local/ [root@jenkins ~]#ll sonar-scanner-cli-4.2.0.1873-linux.zip -rw-r--r-- 1 root root 42397119 Oct 2 00:09 sonar-scanner-cli-4.2.0.1873-linux.zip # 2. 압축 해제 및 심볼릭 링크 생성 [root@jenkins local]#yum install -y unzip [root@jenkins local]#unzip /root/sonar-scanner-cli-4.2.0.1873-linux.zip [root@jenkins local]# ln -s sonar-scanner-4.2.0.1873-linux sonar # 3. 전역 PATH 변수 설정 [root@jenkins ~]# export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 4. 변수를 /etc/profile 에 영구 저장 [root@jenkins ~]# vim /etc/profile .. export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 5. 클라이언트에서 서버로 코드를 스캔해 분석 테스트 /var/lib/jenkins/workspace/test_job/ 디렉터리의 blog 코드를 SonarQube 에 푸시하여 테스트 [root@jenkins ~]#cd /var/lib/jenkins/workspace/test_job/ [root@jenkins test_job]#ll total 8 -rw-r--r-- 1 root root 7 Oct 1 20:32 a.txt -rw-r--r-- 1 root root 3634 Oct 1 20:32 blog_state.txt -rw-r--r-- 1 root root 0 Oct 1 21:12 c.txt drwxr-xr-x 2 root root 6 Oct 1 20:32 myblog drwxr-xr-x 2 root root 6 Oct 1 20:32 myblog-deploy [root@jenkins test_job]#sonar-scanner \\ -Dsonar.projectKey=html \\ -Dsonar.sources=. \\ -Dsonar.host.url=http://11.0.1.202:9000 \\ -Dsonar.login=a3bd8e829343edabbbc58fefe0d3e97fdb12268b 실행 성공:\n서버 확인: 업로드 성공\n","permalink":"https://LMDAWN1127.github.io/ko/posts/sonarqube-practice/","summary":"SonarQube 을 사설로 배포하는 실전 가이드: JDK8/MySQL5.6 설치, 플러그인 포함 압축 해제, 비 root 사용자 실행, 웹 토큰 설정, sonar-scanner 클라이언트 코드 스캔.","title":"SonarQube 실전: 사설 배포, 토큰 설정, 코드 품질 스캔"},{"content":"01. Jenkins 소개 공식 사이트: jenkins.io\nJenkins 은 오픈소스 프로젝트로, Java 기반의 지속적 통합(CI) 도구다. 반복 작업을 모니터링하고 개방적이고 사용하기 쉬운 소프트웨어 플랫폼을 제공하여 소프트웨어 CI를 가능하게 한다。\n02. Jenkins 배포 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 I. 환경 준비 1、OS: CentOS 7.9 2、메모리: 실습 2G, 운영 최소 6G 3、방화벽 비활성화 및 SELinux 끄기 4、JDK17 II. JDK 설치 RPM 패키지를 업로드 [root@jenkins ~]#ll total 178212 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm [root@jenkins ~]#rpm -ivh jdk-17_linux-x64_bin.rpm warning: jdk-17_linux-x64_bin.rpm: Header V3 RSA/SHA256 Signature, key ID ad986da3: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jdk-17-2000:17.0.11-7 ################################# [100%] 설치 성공 여부 확인: [root@jenkins ~]# rpm -qa|grep jdk jdk-17-17.0.11-7.x86_64 II. Jenkins 설치 Jenkins 패키지를 업로드 [root@jenkins ~]#ll total 269432 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm -rw-r--r-- 1 root root 93405530 Oct 1 19:06 jenkins-2.405-1.1.noarch.rpm [root@jenkins ~]#rpm -ivh jenkins-2.405-1.1.noarch.rpm warning: jenkins-2.405-1.1.noarch.rpm: Header V4 RSA/SHA512 Signature, key ID ef5975ca: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jenkins-2.405-1.1 ################################# [100%] III. Jenkins 실행 사용자를 root 로 변경 1) 설정 파일의 실행 사용자 변경 [root@jenkins ~]# vim /etc/sysconfig/jenkins ... JENKINS_USER=\u0026#34;root\u0026#34; ... 2) 시작 설정의 사용자 변경 [root@jenkins ~]# vim /usr/lib/systemd/system/jenkins.service ... User=root Group=root ... [root@jenkins ~]# systemctl daemon-reload 3) 폰트 설정 설치 [root@jenkins ~]# yum -y install fontconfig IV. Jenkins 시작 및 부팅 시 자동 시작 설정 [root@jenkins ~]# systemctl start jenkins [root@jenkins ~]# systemctl enable jenkins 03. Jenkins 로그인 04. 비밀번호 변경 비밀번호 변경 후 재로그인, 사용자 이름은 admin\n05. 플러그인 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1. 플러그인을 Jenkins 홈 디렉터리에 업로드 [root@jenkins ~]# cd /var/lib/jenkins/ [root@jenkins ~]#ll total 576228 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm -rw-r--r-- 1 root root 93405530 Oct 1 19:06 jenkins-2.405-1.1.noarch.rpm -rw-r--r-- 1 root root 314156543 Oct 1 19:30 jenkins_plu.tar.gz 2. 플러그인을 plugins 디렉터리에 압축 해제 [root@jenkins jenkins]#tar xf /root/jenkins_plu.tar.gz -C plugins/ 3. Jenkins 재시작하여 적용 [root@jenkins ~]# systemctl restart jenkins 06. 프로젝트 생성 프리스타일 프로젝트를 만들고 pwd 를 실행하여 Jenkins 가 사용하는 기본 작업 디렉터리 확인\n참고: Jenkins 가 프로젝트를 만들면 홈 /var/lib/jenkins/ 아래에 workspace 디렉터리를 자동으로 만들고, 그 안에 프로젝트 이름의 디렉터리를 생성한다.\n이후 Jenkins 가 GitLab 에서 가져온 코드는 이 프로젝트 디렉터리에 저장된다.\n1 2 [root@jenkins ~]#ls /var/lib/jenkins/workspace/ test_job GitLab 과 Jenkins 연결\n연결 전에는 연결 불가 메시지가 표시됨:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # 키 인증(키리스) 설정 [root@jenkins ~]#ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory \u0026#39;/root/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:Rr4ouinx/cPqVxJsxju2Cjd1mhWmWSuKGOKG532uTvk root@jenkins The key\u0026#39;s randomart image is: +---[RSA 2048]----+ | | | | | o = | | *B o | |o o=oS | |=o . +=O.. | |o=+.Bo+=. | |oo.B.+* | | .=+BOE. | +----[SHA256]-----+ [root@jenkins ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+kaj/NEwHKaIErMEClVYuG3JuWr9CD7XpuO5YZ6JftMmNdAPMrvS0mWqP4SDyT6/le0j1RGkA48GsirESfMqXKvzSSd5AuhIPmUADtGPNK6VSeiMKyoWgiNBFLn6RIOAcXI0MuZrYQMLJz+kOYr6kF9em/6MfFQrLkt9DzIlnR9b7CMLyDnPOZ8FG640DqqX0sZ8G+i2v41VX/c7RGIWAgEhJY+kiMS5cDxWlTf2k8hoZmh0VIa/G9AwnvbklyUiE4ACd9GtEJG5xXJzedn7q0/Ygfr26T29t+KST3SlyrXIaEbEC5WwIMymCEJDCPPCGzqLjsWa+4dbJhoEM1afV root@jenkins # Jenkins 에 git 설치 [root@jenkins ~]#yum install -y git GitLab 에 Jenkins 가 생성한 키 추가:\nJenkins 에서 한 번 git clone 하여 최초 yes 입력 완료\n1 2 3 4 5 6 7 8 9 10 11 12 13 [root@jenkins ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. remote: Enumerating objects: 20, done. remote: Counting objects: 100% (9/9), done. remote: Compressing objects: 100% (8/8), done. remote: Total 20 (delta 0), reused 0 (delta 0), pack-reused 11 Receiving objects: 100% (20/20), done. Resolving deltas: 100% (2/2), done. 이제 연결됨\n다시 빌드하여 풀 테스트\n풀 성공\n웹훅 설정\n웹훅을 설정하여 GitLab 코드를 Jenkins 가 자동으로 풀하고 빌드하도록 구성\n양쪽 저장 후 테스트\n프로젝트 수정 후 다시 푸시하여 테스트\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [root@gitlab blog]#git branch * master [root@gitlab blog]#touch c.txt [root@gitlab blog]#git add . [root@gitlab blog]#git commit -m \u0026#34;test_v2\u0026#34; [master 7b1f29e] test_v2 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 c.txt [root@gitlab blog]#git pull origin master remote: Enumerating objects: 17, done. remote: Counting objects: 100% (9/9), done. remote: Compressing objects: 100% (8/8), done. remote: Total 17 (delta 0), reused 0 (delta 0), pack-reused 8 Unpacking objects: 100% (17/17), done. From 11.0.1.200:dawn/blog * branch master -\u0026gt; FETCH_HEAD Merge made by the \u0026#39;recursive\u0026#39; strategy. a.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 a.txt [root@gitlab blog]#git push -u origin master Counting objects: 7, done. Delta compression using up to 2 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (5/5), 469 bytes | 0 bytes/s, done. Total 5 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git 47f606c..91e80a5 master -\u0026gt; master Branch master set up to track remote branch master from origin. 자동 빌드 성공\n","permalink":"https://LMDAWN1127.github.io/ko/posts/jenkins-practice/","summary":"Jenkins 을 사설로 배포하는 실전 가이드: JDK/GitLab RPM 설치, root 계정 실행, 오프라인 플러그인 설치, GitLab SSH 키 인증과 webhook 자동 빌드 연동.","title":"Jenkins 실전: 사설 배포, 플러그인 설치, GitLab 연동 CI"},{"content":"01. GitLab 관련 사이트 공식 사이트: https://about.gitlab.com/\n국내 미러: https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/\n02. GitLab 배포 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 I. 환경 준비 1、OS: CentOS 7.9 2、메모리: 실습 4G, 운영 최소 8G 3、방화벽 비활성화 및 SELinux 끄기 II. GitLab RPM 패키지 다운로드 [root@gitlab ~]# wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm III. GitLab 설치 # 설치 중 의존성 설치가 필요함 [root@gitlab ~]# rpm -ivh gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm warning: gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID f27eab47: NOKEY error: Failed dependencies: policycoreutils-python is needed by gitlab-ce-16.9.8-ce.0.el7.x86_64 # 의존성 설치 [root@gitlab ~]# yum -y install policycoreutils-python # 의존성 설치 후 GitLab 설치 계속 [root@gitlab ~]# rpm -ivh gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm warning: gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID f27eab47: NOKEY Preparing... ################################# [100%] Updating / installing... 1:gitlab-ce-16.9.8-ce.0.el7 ################################# [100%] IV. 시작 전 도메인 또는 IP 접근 설정 [root@gitlab ~]# vim /etc/gitlab/gitlab.rb ... external_url \u0026#39;http://11.0.1.200\u0026#39; ... 설정 후 다음 명령 실행: [root@gitlab ~]# gitlab-ctl reconfigure 03. GitLab 구성 요소 1 2 3 4 5 6 7 nginx： 정적 웹 서버 gitlab-workhorse: 경량 리버스 프록시 logrotate： 로그 파일 관리 도구 postgresql： 데이터베이스 redis： 캐시 데이터베이스 sidekiq： 백그라운드 큐 작업 실행(비동기). (Ruby) unicorn: Rack 애플리케이션용 HTTP 서버이며, GitLab Rails 앱이 이 서버에서 호스팅됨. (Ruby 웹 서버, 주로 Ruby로 작성) 04. GitLab 자주 쓰는 명령어 1 2 3 4 5 gitlab-ctl start # GitLab 시작 (gitlab-ctl reconfigure 후 서비스 자동 시작) gitlab-ctl status # GitLab 전체 서비스 운영 상태 확인 gitlab-ctl stop # GitLab 서비스 정지 gitlab-ctl stop nginx # 개별 서비스 정지 gitlab-ctl tail # 전체 서비스 로그 보기 05. GitLab 웹 설정 GitLab 로그인: 11.0.1.200\n1. 로그인 비밀번호 gitlab-ctl reconfigure 완료 후 비밀번호 저장 위치를 안내함:\n2. UI 언어를 중국어로 설정 3. 기본 비밀번호 변경 4. 웹훅용 로컬 네트워크 요청 허용 06. GitLab 실전 1. 그룹 만들기 2. 그룹 아래 프로젝트 생성 첫 번째 코드 저장소가 만들어졌지만 비어 있음. 이어서 코드 업로드\n3. 프로젝트에 코드 커밋 프로젝트 생성 후 blog 코드를 이 프로젝트에 업로드해야 함\n서버와 코드 저장소 연결 방식은 두 가지\n첫째: 사용자 이름과 비밀번호로 연결\n둘째: SSH 키 인증(키리스) 방식으로 연결\n우리는 키 인증 방식 사용: 편리하고 보안성이 높음\n키 페어 생성(여기서는 GitLab 서버를 직접 사용) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [root@gitlab ~]#ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory \u0026#39;/root/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:j3FozajJdufxM0ZRtgPa2ZNSwWKvo8bvxbMJl8F5V3M root@gitlab The key\u0026#39;s randomart image is: +---[RSA 2048]----+ | ... | | + = | | + X +E| | =. =.B.+| | S + ++oo| | . + = +. +.| | = o.+o..* | | . . o+o++ + | | ..++oo | +----[SHA256]-----+ 키 페어 확인: [root@gitlab ~]# ll .ssh/ total 8 -rw------- 1 root root 1679 May 14 15:02 id_rsa # 개인키 -rw-r--r-- 1 root root 393 May 14 15:02 id_rsa.pub # 공개키 [root@gitlab ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJGpCKL64eidZ3I/nAVNCNJwD8yeDqelZP0GUTwlcp6IdKaZ7wUOIRDCtGn5r+YnOCKNwMhUNzb3VCOSVLDVIE0x53dPrCMK4OKLmZ6OtTYFBveGOZWWwdYyGQITx2eP7zOSBxeW3AnEWcya/UZdiZrYXA/d29DWjMjCt5QAxOit0EWSir1Z4i2MkTPuljOmjk6A55XAoem0WJRdg/d8KKIiCdcGrj4adYq2kNJ8oYH/9QcnpASUW/ANhzrALnLIX2WaH/p0T3gisLvk2KDUe3rVKD3XaKDlyLgWIViQFm3Me6L2e4taRFiu3VdsbfFfzjDevfDtLaqKLOqPtaczlF root@gitlab 생성한 공개키를 GitLab UI의 SSH 키 관리 페이지에 붙여넣기 서버와 GitLab UI 간 SSH 키 인증 완료. 이어서 코드를 blog 프로젝트에 푸시\n코드를 푸시하는 방법은 두 가지:\n빈 저장소를 로컬에 클론한 뒤 코드를 복사하고 원격 GitLab 에 커밋 프로젝트 안에서 프로젝트 URL 확인\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 1. 링크를 복사하여 실행 [root@gitlab ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. warning: remote HEAD refers to nonexistent ref, unable to checkout. 2. blog 저장소가 로컬에 내려받아졌는지 확인 [root@gitlab ~]#ll total 1176964 drwxr-xr-x 3 root root 18 Oct 1 14:43 blog -rw-r--r-- 1 root root 84579612 Oct 1 14:35 blog_backup_20261001-1435.tar.gz drwxr-xr-x 3 root root 18 Sep 30 19:14 dawn -rw-r--r-- 1 root root 1120627456 Sep 30 23:12 gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm 3. blog 디렉터리로 이동 [root@gitlab ~]#cd blog/ 4. 코드 압축 해제: [root@gitlab blog]#tar xf /root/blog_backup_20261001-1435.tar.gz -C . 5. 코드를 로컬 저장소에 커밋 [root@gitlab blog]#git add . [root@gitlab blog]#git commit -m \u0026#34;DAWN’s Blog\u0026#34; [master (root-commit) 5af4246] DAWN’s Blog 3 files changed, 112 insertions(+) create mode 100644 blog_state.txt create mode 160000 myblog create mode 160000 myblog-deploy 6. 코드를 원격 GitLab 의 blog 저장소에 푸시 GitLab 저장소를 직접 클론했으므로 로컬 디렉터리에 원격 blog 프로젝트 주소가 이미 있음 원격 저장소 확인: [root@gitlab blog]#git remote -v origin git@11.0.1.200:dawn/blog.git (fetch) origin git@11.0.1.200:dawn/blog.git (push) 로컬 master 를 원격 origin 에 푸시 [root@gitlab blog]#git push -u origin master Counting objects: 3, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 1.23 KiB | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git * [new branch] master -\u0026gt; master Branch master set up to track remote branch master from origin. blog 프로젝트 확인, 코드 업로드 성공\n로컬 Git 저장소를 원격 GitLab 에 푸시 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 1. 로컬 저장소 구성 [root@gitlab ~]#mkdir dawn [root@gitlab ~]#cd dawn 2. 저장소 초기화 [root@gitlab dawn]#git init 3. 코드 업로드 [root@gitlab dawn]#touch a.txt [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;test_v1\u0026#34; [master (root-commit) 815c600] test_v1 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 a.txt 4. GitLab 의 blog 저장소를 로컬 원격으로 설정 [root@gitlab dawn]#git remote add origin git@11.0.1.200:dawn/blog.git 5. 로컬 내용을 원격에 푸시(원격에 이미 내용이 있어 바로 푸시하면 오류; 원격 내용을 보존하려면 먼저 pull 병합 후 푸시) [root@gitlab dawn]#git pull origin master warning: no common commits remote: Enumerating objects: 3, done. remote: Counting objects: 100% (3/3), done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), done. From 11.0.1.200:dawn/blog * branch master -\u0026gt; FETCH_HEAD Merge made by the \u0026#39;recursive\u0026#39; strategy. blog_state.txt | 110 ++++++++++++++++++++++++++++++++++++ myblog | 1 + myblog-deploy | 1 + 3 files changed, 112 insertions(+) create mode 100644 blog_state.txt create mode 160000 myblog create mode 160000 myblog-deploy [root@gitlab dawn]#git push -u origin master Counting objects: 6, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (5/5), 462 bytes | 0 bytes/s, done. Total 5 (delta 1), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git 5af4246..0242e0a master -\u0026gt; master Branch master set up to track remote branch master from origin. 일반 계정 dev 생성 생성 후 dev 비밀번호 변경 — 로그아웃하지 말고 사용자 화면의 편집 클릭\n이후 dev 로 GitLab 로그인(첫 로그인 시 비밀번호 변경 필요)\ndev 사용자를 dawn 그룹에 추가 dev 서버에서 키 페어 생성 1 [root@lb01 ~]#ssh-keygen 생성한 공개키를 dev 계정의 SSH 키 페이지에 복사 코드를 dev 호스트 11.0.1.5 에 클론 1 2 3 4 5 6 7 8 9 10 11 12 13 [root@lb01 ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. remote: Enumerating objects: 8, done. remote: Counting objects: 100% (8/8), done. remote: Compressing objects: 100% (6/6), done. remote: Total 8 (delta 1), reused 0 (delta 0), pack-reused 0 Receiving objects: 100% (8/8), done. Resolving deltas: 100% (1/1), done. 코드 수정 후 원격 저장소에 업로드 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 11.0.1.5 의 사용자 설정 [root@lb01 blog]#git config --global user.email \u0026#34;dev@mail.com\u0026#34; [root@lb01 blog]#git config --global user.name \u0026#34;dev\u0026#34; [root@lb01 blog]#vim a.txt 123 [root@lb01 blog]#git commit -am \u0026#34;123\u0026#34; 원격 저장소에 커밋 [root@lb01 blog]#git push -u origin master Counting objects: 5, done. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 257 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: GitLab: You are not allowed to push code to protected branches on this project.To git@11.0.1.200:dawn/blog.git ! [remote rejected] master -\u0026gt; master (pre-receive hook declined) error: failed to push some refs to \u0026#39;git@11.0.1.200:dawn/blog.git\u0026#39; # master 브랜치로의 푸시는 허용되지 않음 다른 브랜치에 푸시: 1. dev 브랜치 생성 [root@lb01 blog]#git branch dev [root@lb01 blog]#git branch dev * master 2. dev 브랜치를 원격에 푸시 [root@lb01 blog]#git push -u origin dev 병합 요청(MR)\ndev 계정에서 병합 요청 생성 root 계정에서 병합 승인 후 원본 dev 브랜치 삭제 웹 서버에서 코드 다운로드 1 2 [root@web01 ~]#git clone http://11.0.1.200/dawn/blog.git 사용자 이름과 비밀번호 입력 ","permalink":"https://LMDAWN1127.github.io/ko/posts/gitlab-practice/","summary":"GitLab 을 사설로 배포하는 실전 가이드: RPM 설치, 구성 요소, 자주 쓰는 명령어, 웹 UI 설정, SSH 키 인증, 다중 사용자 협업.","title":"GitLab 실전: 사설 배포, SSH 키 인증, 저장소 관리"},{"content":"01. Git 설치 및 저장소 초기화 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [root@gitlab ~]#yum install -y git [root@gitlab ~]#git --version git version 1.8.3.1 [root@gitlab ~]#git config --global user.name \u0026#34;dawn\u0026#34; [root@gitlab ~]#git config --global user.email \u0026#34;dawn@mail.com\u0026#34; [root@gitlab ~]#git config --global color.ui true [root@gitlab ~]#cat .gitconfig [user] name = dawn email = dawn@mail.com [color] ui = true [root@gitlab ~]#mkdir dawn [root@gitlab ~]#cd dawn [root@gitlab dawn]#ll total 0 [root@gitlab dawn]#git init Initialized empty Git repository in /root/dawn/.git/ [root@gitlab dawn]#ll total 0 [root@gitlab dawn]#ll -a total 0 drwxr-xr-x 3 root root 18 Jun 11 10:57 . dr-xr-x---. 4 root root 174 Jun 11 10:57 .. drwxr-xr-x 7 root root 119 Jun 11 10:57 .git 02. Git 명령어 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 1. 저장소 상태 확인하기 (항상 작업 트리를 깨끗하게 유지) [root@gitlab dawn]#git status # On branch master # # Initial commit # nothing to commit (create/copy files and use \u0026#34;git add\u0026#34; to track) 2. 일반 파일 생성하기 [root@gitlab dawn]#touch a.txt 3. 작업 트리의 내용을 스테이징하기 [root@gitlab dawn]#git add a.txt # git add . 는 작업 트리의 모든 내용을 스테이징 영역에 올린다 [root@gitlab dawn]#git add . 4. 스테이징 영역의 내용 삭제하기 [root@gitlab dawn]#git rm --cached a.txt rm \u0026#39;a.txt\u0026#39; 5. 스테이징 후 로컬 저장소에 커밋하기 [root@gitlab dawn]#git add a.txt [root@gitlab dawn]#git status # On branch master # # Initial commit # # Changes to be committed: # (use \u0026#34;git rm --cached \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # new file: a.txt # # git commit -m 뒤에 메시지를 붙여 로컬 저장소에 커밋한다 [root@gitlab dawn]#git commit -m \u0026#34;create a.txt\u0026#34; [master (root-commit) b65d348] create a.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 a.txt 상태 확인: [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 파일 생성 touch a.txt 스테이징 git add a.txt 로컬 저장소에 커밋 git commit -m \u0026#34;xxx\u0026#34; 2. git 으로 삭제하기 [root@gitlab dawn]#git rm a.txt rm \u0026#39;a.txt\u0026#39; [root@gitlab dawn]#git status # On branch master # Changes to be committed: # (use \u0026#34;git reset HEAD \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # deleted: a.txt # [root@gitlab dawn]#git commit -m \u0026#34;del a.txt\u0026#34; [master 41fc71b] del a.txt 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 a.txt [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 참고: 작업 트리 파일을 스테이징한 뒤 취소하려면 스테이징 영역에서 제거한다 [root@gitlab dawn]#touch c.txt [root@gitlab dawn]#git add c.txt [root@gitlab dawn]#git rm --cached c.txt rm \u0026#39;c.txt\u0026#39; 3. 작업 트리 내용 복구하기 [root@gitlab dawn]#git reset HEAD b.txt [root@gitlab dawn]#git checkout -- b.txt 4. git diff 로 비교하기 git diff 작업 트리와 스테이징 영역의 차이를 비교한다 [root@gitlab dawn]#git diff diff --git a/b.txt b/b.txt index e69de29..ccc3e7b 100644 --- a/b.txt +++ b/b.txt @@ -0,0 +1 @@ +aaaaa git diff --cached 스테이징 영역과 로컬 저장소의 차이를 비교한다 [root@gitlab dawn]#git diff --cached diff --git a/b.txt b/b.txt index e69de29..ccc3e7b 100644 --- a/b.txt +++ b/b.txt @@ -0,0 +1 @@ +aaaaa 5. 파일 이름 변경하기 [root@gitlab dawn]#git mv b.txt b.bak [root@gitlab dawn]#git status # On branch master # Changes to be committed: # (use \u0026#34;git reset HEAD \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # renamed: b.txt -\u0026gt; b.bak # [root@gitlab dawn]#git commit -m \u0026#34;newname b.txt--\u0026gt;b.bak\u0026#34; [master 55335a7] newname b.txt--\u0026gt;b.bak 1 file changed, 0 insertions(+), 0 deletions(-) rename b.txt =\u0026gt; b.bak (100%) [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 6. 특정 스냅샷으로 롤백하기 모든 스냅샷 확인: [root@gitlab dawn]#git log --oneline ae6585c xx 55335a7 newname b.txt--\u0026gt;b.bak 09ffe53 modifile b.txt 043a297 newfile b.txt 41fc71b del a.txt b65d348 create a.txt [root@gitlab dawn]#ll total 8 -rw-r--r-- 1 root root 6 Jun 11 12:10 b.bak -rw-r--r-- 1 root root 6 Jun 11 12:07 b.txt git reset --hard 로 롤백하기 [root@gitlab dawn]#git reset --hard b65d348 HEAD is now at b65d348 create a.txt [root@gitlab dawn]#ll total 0 -rw-r--r-- 1 root root 0 Jun 11 12:12 a.txt 7. 지금까지의 모든 커밋 작업 확인: [root@gitlab dawn]#git reflog b65d348 HEAD@{0}: reset: moving to b65d348 ae6585c HEAD@{1}: commit: xx 55335a7 HEAD@{2}: commit: newname b.txt--\u0026gt;b.bak 09ffe53 HEAD@{3}: commit: modifile b.txt 043a297 HEAD@{4}: commit: newfile b.txt 41fc71b HEAD@{5}: commit: del a.txt b65d348 HEAD@{6}: commit (initial): create a.txt [root@gitlab dawn]#git reset --hard ae6585c HEAD is now at ae6585c xx [root@gitlab dawn]#ll total 8 -rw-r--r-- 1 root root 6 Jun 11 12:16 b.bak -rw-r--r-- 1 root root 6 Jun 11 12:16 b.txt Git 브랜치 1. 브랜치 목록 확인하기 (* 는 현재 브랜치) [root@gitlab dawn]#git branch * master 2. 브랜치 생성하기 [root@gitlab dawn]#git branch dev [root@gitlab dawn]#git branch dev * master 3. dev 브랜치로 전환하기 [root@gitlab dawn]#git checkout dev Switched to branch \u0026#39;dev\u0026#39; [root@gitlab dawn]#git branch * dev master 4. 브랜치에서 파일을 만들고 로컬 저장소에 커밋하기 [root@gitlab dawn]#touch aa.txt [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;newfile aa.txt\u0026#34; [dev 2694c7f] newfile aa.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 aa.txt 5. 브랜치 병합하기 먼저 master 브랜치로 되돌아간다 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master dev 브랜치를 master 에 병합한다 [root@gitlab dawn]#git merge dev Updating ae6585c..2694c7f Fast-forward aa.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 aa.txt 6. 기능 구현이 끝나면 브랜치를 즉시 삭제한다 (원격 브랜치에 뒤처지기 때문) [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 2694c7f). [root@gitlab dawn]#git branch * master 7. 충돌 병합 ---------------------------------- 파일 내용을 수정했다고 해서 병합이 막히는 것은 아니다 master 브랜치의 파일을 수정해 bbbbb 를 추가한다 [root@gitlab dawn]#vim b.txt aaaaa bbbbb 한 번의 명령으로 dev 브랜치 생성과 전환을 동시에 할 수 있다 [root@gitlab dawn]#git checkout -b dev Switched to a new branch \u0026#39;dev\u0026#39; [root@gitlab dawn]# [root@gitlab dawn]# [root@gitlab dawn]#git branch * dev master b.txt 의 내용을 수정한다 [root@gitlab dawn]#vim b.txt aaaaa bbccc 로컬 저장소에 커밋하기 [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;modifle bbbccc\u0026#34; master 브랜치로 돌아가 dev 브랜치를 병합한다 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master [root@gitlab dawn]#cat b.txt aaaaa bbbbb [root@gitlab dawn]#git merge dev Updating f56faba..dc17f6d Fast-forward b.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) [root@gitlab dawn]#cat b.txt aaaaa bbccc ------------------------------------------------------ 충돌이 발생하는 경우 dev 브랜치 생성하기 [root@gitlab dawn]#git branch dev master 브랜치에서 b.txt 의 3번째 줄을 수정한다 [root@gitlab dawn]#cat b.txt aaaaa iiiii bbbbb [root@gitlab dawn]#git commit -am \u0026#34;modifile b.txt bbbbb\u0026#34; [master ae4ab87] modifile b.txt bbbbb 1 file changed, 1 insertion(+) dev 브랜치로 전환해 b.txt 의 3번째 줄을 수정한다 [root@gitlab dawn]#cat b.txt aaaaa iiiii ccccc 로컬 저장소에 커밋하기 [root@gitlab dawn]#git commit -am \u0026#34;modifile b.txt ccccc\u0026#34; [dev 925c06c] modifile b.txt ccccc 1 file changed, 1 insertion(+) master 브랜치로 전환해 브랜치를 병합한다 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master [root@gitlab dawn]#git merge dev Auto-merging b.txt CONFLICT (content): Merge conflict in b.txt Automatic merge failed; fix conflicts and then commit the result. 파일을 확인하면 충돌 표시가 보인다 [root@gitlab dawn]#cat b.txt aaaaa iiiii \u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt; HEAD bbbbb ======= ccccc \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; dev 수동으로 충돌을 해결해 한쪽만 남기거나 모두 남긴다 [root@gitlab dawn]#vim b.txt aaaaa iiiii bbbbb ccccc [root@gitlab dawn]#git commit -am \u0026#34;merge dev\u0026#34; [master 325eef2] merge dev [root@gitlab dawn]# [root@gitlab dawn]# [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean [root@gitlab dawn]#cat b.txt aaaaa iiiii bbbbb ccccc 병합 후 dev 브랜치를 삭제한다 [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 925c06c). ","permalink":"https://LMDAWN1127.github.io/ko/posts/git-practice-notes/","summary":"설치와 기본 명령어, 브랜치 운영, 충돌 해결까지 다루는 Git 실습 기록.","title":"Git 실전 노트: 설치부터 기본 명령어와 브랜치 관리까지"},{"content":"1. 로그인 사용자 수 확인하기 1 2 3 4 5 6 7 [root@web02 ~]#w 17:44:18 up 1:17, 2 users, load average: 0.01, 0.02, 0.05 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root pts/0 11.0.1.1 16:31 11:46 0.07s 0.07s -bash root pts/1 11.0.1.52 17:07 2.00s 0.04s 0.00s w [root@web02 ~]#w|awk \u0026#39;NR==1{print $4}\u0026#39; 2 2. Zabbix 모니터링 항목으로 정의하기 1 2 3 4 5 6 7 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; # zabbix_agentd -p 로 항목 문법 검사가 가능합니다 [root@web02 ~]#zabbix_agentd -p|grep login_number login_number [t|2] # 에이전트 재시작 [root@web02 ~]# systemctl restart zabbix-agent.service 3. Zabbix 서버 측에서 수집·모니터링하기 서버에서 명령줄로 모니터링 항목을 통해 사용자 정의 값을 가져올 수 있는지 테스트합니다.\n1 2 3 4 # zabbix-get 명령 설치 [root@zabbix ~]# yum -y install zabbix-get [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k login_number 2 웹 페이지에서 모니터링 항목 추가\n모니터링 항목 확인\n4. TCP 22 포트 생존 여부 모니터링 모니터링하려는 정보에 맞는 키 값을 선택합니다.\nTCP 포트가 LISTEN 상태인지 확인합니다. 반환값: 0 - 대기 안 함; 1 - 대기 중.\nweb02의 sshd 서비스 중지\n1 [root@web02 ~]# systemctl stop sshd 5. 표시용 커스텀 값 매핑 구성 값 매핑 적용\n6. 서버 메모리 비율 커스텀 모니터링 1단계: 커스텀 항목을 정의하여 가용률 가져오기\n1 2 3 4 5 6 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; UserParameter=mem_free,free|awk \u0026#39;NR==2{print $NF/$2*100}\u0026#39; [root@web02 ~]# systemctl restart zabbix-agent.service [root@web02 ~]#zabbix_agentd -p|grep mem_free mem_free [t|79.8753] 2단계: 서버에서 키 값 가져오기를 테스트하고 항목 추가\n1 2 [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k mem_free 79.9775 7. 가용 메모리에 트리거 설정 가용 메모리 비율이 20% 미만이면 알림을 발보합니다.\n테스트: swap 파티션 끄기\n1 [root@web02 ~]# swapoff -a dd로 부하 테스트를 실행해 메모리 소비\n1 [root@web02 ~]# dd if=/dev/zero of=/dev/null bs=1200M count=2048 8. 다중 조건 트리거 구성 두 조건이 동시에 만족할 때만 알림을 발보합니다.\nswap 가용률 추가\n1 2 3 4 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; UserParameter=mem_free,free|awk \u0026#39;NR==2{print $NF/$2*100}\u0026#39; UserParameter=swap_free,free -m|awk \u0026#39;NR==3{print $3/$2*100}\u0026#39; 트리거 설정 수정\n이후 dd 부하 테스트를 다시 실행해 결과를 확인합니다.\n","permalink":"https://LMDAWN1127.github.io/ko/posts/zabbix-custom-monitor/","summary":"Zabbix 커스텀 모니터링 실전 — UserParameter로 로그인 사용자 수·메모리 가용률 등 사용자 정의 메트릭을 수집하고 zabbix_get으로 검증한 뒤, 웹 UI에서 모니터링 항목·값 매핑·트리거(다중 조건 트리거 포함)를 추가해 커스텀 알림을 구성합니다.","title":"Zabbix 커스텀 모니터링"},{"content":"기업 Zabbix 고부하 시나리오에서 사용합니다.\n1. 독립 데이터베이스 서버(11.0.1.52) 준비 및 MariaDB 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@db02 ~]#yum -y install mariadb-server [root@db02 ~]#systemctl enable mariadb --now [root@db02 ~]#mysql Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 2 Server version: 5.5.68-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [(none)]\u0026gt; create database zabbix character set utf8 collate utf8_bin; Query OK, 1 row affected (0.02 sec) MariaDB [(none)]\u0026gt; create user \u0026#39;zabbix\u0026#39;@\u0026#39;%\u0026#39; identified by \u0026#39;zabbix\u0026#39;; Query OK, 0 rows affected (0.10 sec) MariaDB [(none)]\u0026gt; grant all privileges on zabbix.* to \u0026#39;zabbix\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) 2. Zabbix 서버에서 zabbix 데이터베이스를 내보내 새 데이터베이스로 가져오기 1 2 [root@zabbix ~]# mysqldump -uroot -B zabbix \u0026gt; zabbix.sql [root@zabbix ~]# cat zabbix.sql |mysql -h 172.16.1.52 -uzabbix -pzabbix zabbix 3. Web UI의 데이터베이스 연결 설정 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@zabbix ~]# systemctl stop mariadb [root@zabbix ~]# cat /etc/zabbix/web/zabbix.conf.php \u0026lt;?php // Zabbix GUI configuration file. global $DB; $DB[\u0026#39;TYPE\u0026#39;] = \u0026#39;MYSQL\u0026#39;; $DB[\u0026#39;SERVER\u0026#39;] = \u0026#39;172.16.1.52\u0026#39;; $DB[\u0026#39;PORT\u0026#39;] = \u0026#39;0\u0026#39;; $DB[\u0026#39;DATABASE\u0026#39;] = \u0026#39;zabbix\u0026#39;; $DB[\u0026#39;USER\u0026#39;] = \u0026#39;zabbix\u0026#39;; $DB[\u0026#39;PASSWORD\u0026#39;] = \u0026#39;zabbix\u0026#39;; [root@zabbix ~]# grep ^DB /etc/zabbix/zabbix_server.conf DBHost=172.16.1.52 DBName=zabbix DBUser=zabbix DBPassword=zabbix 4. 서비스 재시작 1 [root@zabbix ~]# systemctl restart zabbix-server httpd ","permalink":"https://LMDAWN1127.github.io/ko/posts/zabbix-split-database/","summary":"기업 Zabbix 고부하 시나리오용 — Zabbix 서버의 데이터베이스를 전용 MariaDB 서버(11.0.1.52)로 분리합니다. 데이터베이스 생성 및 권한 부여, 데이터 내보내기/가져오기, Web UI와 Server의 데이터베이스 연결 설정 변경, 그리고 서비스 재시작까지 다룹니다.","title":"Zabbix 데이터베이스 분리"},{"content":"1. zabbix-agent 클라이언트 설치 — 서버(11.0.1.8) 준비 1 2 3 4 5 6 7 8 저장소를 설정합니다: [root@web02 ~]# rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 서버의 yum 저장소를 클라이언트로 동기화합니다: [root@zabbix ~]#scp /etc/yum.repos.d/CentOS-SCLo-scl.repo root@172.16.1.8:/etc/yum.repos.d/CentOS-SCLo-scl.repo [root@zabbix ~]#scp /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo root@172.16.1.8:/etc/yum.repos.d/CentOS-SCLo-scl-rh.repo [root@zabbix ~]#scp /etc/yum.repos.d/zabbix.repo root@172.16.1.8:/etc/yum.repos.d/zabbix.repo 설치 시 패키지 충돌이 발생하면: [root@web02 ~]# yum -y install centos-release-scl 2. zabbix-agent에 서버 정보를 설정하고 시작 1 2 3 4 5 6 [root@web02 ~]# yum -y install zabbix-agent [root@web02 ~]# vim /etc/zabbix/zabbix_agentd.conf Server=172.16.1.71 ServerActive=172.16.1.71 Hostname=web02 [root@web02 ~]# systemctl enable zabbix-agent --now 3. Zabbix에서 web02 모니터링 4. web02 모니터링 설정 완료 ","permalink":"https://LMDAWN1127.github.io/ko/posts/zabbix-monitor-server/","summary":"대상 서버(web02, 11.0.1.8)에 Zabbix 에이전트를 설치하고 Zabbix 서버에 연결한 뒤 웹 UI에서 모니터링을 시작하는 방법입니다.","title":"Zabbix로 서버 정보 모니터링하기"},{"content":"1. 저장소 설정 1 rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 2. zabbix-server 및 zabbix-agent 설치 1 yum -y install zabbix-server-mysql zabbix-agent 3. SCL 저장소 설치 (버전 충돌 해결) 1 [root@zabbix ~]# yum -y install centos-release-scl 4. 프론트엔드 저장소 활성화 1 2 3 4 5 [root@zabbix ~]# vim /etc/yum.repos.d/zabbix.repo [zabbix-frontend] ... enabled=1 ... 5. CentOS-SCLo-scl.repo / CentOS-SCLo-scl-rh.repo 의 깨진 mirrorlist를 Aliyun baseurl로 교체 1 2 3 4 5 6 7 8 9 10 [root@zabbix yum.repos.d]#vim CentOS-SCLo-scl-rh.repo [centos-sclo-rh] ... baseurl=https://mirrors.aliyun.com/centos/7/sclo/x86_64/rh ... [root@zabbix yum.repos.d]#vim CentOS-SCLo-scl.repo [centos-sclo-sclo] ... baseurl=https://mirrors.aliyun.com/centos/7/sclo/x86_64/sclo ... 6. PHP/Apache 프론트엔드 설치 1 [root@zabbix ~]# yum -y install zabbix-web-mysql-scl zabbix-apache-conf-scl 7. MariaDB 데이터베이스 설치 1 2 3 [root@zabbix ~]# yum -y install mariadb-server 데이터베이스를 시작합니다: [root@zabbix ~]#systemctl enable mariadb --now 8. Zabbix 데이터베이스 생성 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@zabbix ~]#mysql -uroot Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 7 Server version: 5.5.68-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [(none)]\u0026gt; create database zabbix character set utf8 collate utf8_bin; Query OK, 1 row affected (0.00 sec) MariaDB [(none)]\u0026gt; create user zabbix@localhost identified by \u0026#39;zabbix\u0026#39;; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; grant all privileges on zabbix.* to zabbix@localhost; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; set global log_bin_trust_function_creators = 1; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; quit Bye 9. 데이터베이스 초기화 (Zabbix 구동 전 필요한 스키마 가져오기) 1 2 3 4 5 [root@zabbix ~]# zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix # zabbix 비밀번호 입력 프롬프트가 나타납니다 [root@zabbix ~]# mysql -uroot MariaDB [(none)]\u0026gt; set global log_bin_trust_function_creators = 0; quit 10. Zabbix 데이터베이스 연결 설정 1 2 3 4 [root@zabbix ~]# egrep ^DB /etc/zabbix/zabbix_server.conf DBName=zabbix DBUser=zabbix DBPassword=zabbix # 주석을 해제하고 비밀번호를 설정 11. 시간대 설정 1 2 [root@zabbix ~]# tail -1 /etc/opt/rh/rh-php72/php-fpm.d/zabbix.conf php_value[date.timezone] = Asia/Shanghai 12. 서비스(server, agent, httpd, php) 시작 및 부팅 시 자동 시작 설정 1 [root@zabbix ~]# systemctl enable zabbix-server zabbix-agent httpd rh-php72-php-fpm --now 13. 11.0.1.71/zabbix 에서 Zabbix 웹 UI 접속 14. Zabbix 설치 완료 15. 중국어로 전환하고 폰트 재설정 Windows 폰트를 찾아 Zabbix의 폰트 디렉터리에 복사합니다.\nWindows 폰트 저장 위치:\n폰트 중 하나를 업로드한 뒤 이름을 변경합니다:\n1 2 3 4 5 6 7 8 9 10 [root@zabbix ~]#cd /usr/share/zabbix/assets/fonts/ [root@zabbix fonts]#ls graphfont.ttf simhei.ttf # 업로드한 폰트 [root@zabbix fonts]#rm -f graphfont.ttf # 기존 폰트 삭제 [root@zabbix fonts]#ln -s /usr/share/zabbix/assets/fonts/simhei.ttf graphfont.ttf # 업로드한 폰트에 연결 [root@zabbix fonts]#chmod 644 simhei.ttf [root@zabbix fonts]#ll total 9520 lrwxrwxrwx 1 root root 41 Sep 23 01:19 graphfont.ttf -\u0026gt; /usr/share/zabbix/assets/fonts/simhei.ttf -rw-r--r-- 1 root root 9745792 Sep 23 01:18 simhei.ttf ","permalink":"https://LMDAWN1127.github.io/ko/posts/zabbix-install/","summary":"CentOS 7에 Zabbix 5.0을 설치하는 단계별 가이드입니다. 저장소 설정, 서버/에이전트/프론트엔드 설치, MariaDB 구축, 스키마 가져오기, 웹 UI의 중국어 전환 및 폰트 설정까지 다룹니다.","title":"Zabbix 설치 및 배포"},{"content":"계획은 다음과 같습니다:\nMaster 노드: 호스트명: master IP 주소: 192.168.8.30\nWorker01 노드: 호스트명: node1 IP 주소: 192.168.8.31\nWorker02 노드: 호스트명: node2 IP 주소: 192.168.8.32\n세 노드 모두에서 다음 단계를 완료하고, 세 노드의 시간이 일치하는지 확인하세요.\n1. 방화벽 및 SELinux 비활성화 1 2 3 4 [root@master ~]# systemctl disable firewalld.service --now [root@master ~]# vim /etc/selinux/config SELINUX=disabled [root@master ~]# setenforce 0 2. 스왑 비활성화 1 2 [root@master ~]# sed -i \u0026#39;/swap/s/^/#/\u0026#39; /etc/fstab [root@master ~]# swapoff -a 3. 시스템 파라미터 설정 및 커널 모듈 로드 1 2 3 4 5 6 7 8 9 10 11 12 [root@master ~]# cat \u0026gt; /etc/modules-load.d/k8s.conf \u0026lt;\u0026lt; EOF overlay br_netfilter EOF [root@master ~]# modprobe overlay [root@master ~]# modprobe br_netfilter [root@master ~]# cat \u0026gt; /etc/sysctl.d/k8s.conf \u0026lt;\u0026lt; EOF net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF [root@master ~]# sysctl --system 4. 컨테이너 런타임(Containerd) 설치 Kubernetes v1.30에서는 컨테이너 런타임으로 Containerd 사용을 권장합니다：\n1 [root@master ~]# yum -y remove runc OS에 기본 포함된 yum 저장소 파일을 삭제합니다：\n1 [root@master ~]# rm -rf /etc/yum.repos.d/Cent*.repo 로컬 yum 저장소를 구성합니다：\n1 2 3 4 5 6 7 8 9 10 11 12 [root@master ~]# mount /dev/cdrom /media/ [root@master ~]# cat /etc/fstab | grep iso9660 /dev/cdrom\t/media\tiso9660 defaults 0 0 [root@master ~]# cat /etc/yum.repos.d/dvd.repo [BaseOS] name=BaseOS baseurl=file:///media/BaseOS gpgcheck=0 [AppStream] name=AppStream baseurl=file:///media/AppStream gpgcheck=0 Containerd를 구성합니다：\n1 2 [root@master ~]# yum install -y yum-utils [root@master ~]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo containerd.io를 설치합니다：\n1 2 3 4 5 6 7 8 9 10 [root@master ~]# yum install -y containerd.io # 설정 파일을 생성하고 편집합니다 [root@master ~]# mkdir -p /etc/containerd [root@master ~]# containerd config default \u0026gt; /etc/containerd/config.toml # systemd cgroup 드라이버를 사용하도록 설정을 편집합니다 #sed -i \u0026#39;s/SystemdCgroup = false/SystemdCgroup = true/g\u0026#39; /etc/containerd/config.toml # 이미지 가속 구성(선택 사항) #sed -i \u0026#39;s|registry.k8s.io|registry.aliyuncs.com/google_containers|g\u0026#39; /etc/containerd/config.toml # 시작하고 부팅 시 활성화합니다 #systemctl enable --now containerd 5. Kubernetes 설치 및 구성 1 2 3 4 5 6 7 [root@master yum.repos.d]# cat kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm/ enabled=1 gpgcheck=1 gpgkey=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm/repodata/repomd.xml.key 지정한 버전의 Kubernetes 구성 요소를 설치합니다：\n1 2 3 [root@master ~]# yum install -y kubelet-1.30.0 kubeadm-1.30.0 kubectl-1.30.0 --disableexcludes=kubernetes # kubelet 시작 [root@master ~]# systemctl enable --now kubelet 6. Kubernetes 초기화 주의：모든 노드에서 위 구성을 마친 후, 다음 단계는 master 노드에서만 실행하세요.\n1 2 3 4 5 [root@master ~]# kubeadm init --pod-network-cidr=10.244.0.0/16 --service-cidr=172.16.0.0/16 --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.30.0 --control-plane-endpoint=\u0026#34;192.168.8.30:6443\u0026#34; --upload-certs # kubectl 구성 [root@master ~]# mkdir -p $HOME/.kube [root@master ~]# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config [root@master ~]# chown $(id -u):$(id -g) $HOME/.kube/config 7. 워커 노드를 Kubernetes 클러스터에 가입 다음 명령을 워커 노드에서 실행합니다. 주의：노드마다 토큰과 CA 인증서 해시가 다를 수 있으므로 자신의 시스템 프롬프트를 따르세요. 인증서가 만료되었을 수도 있으며, 만료된 경우 kubeadm token create --print-join-command로 가입 명령을 다시 생성하세요.\n1 2 [root@node1 ~]# kubeadm join 192.168.8.30:6443 --token yfjrsi.60lmqgzobp0b8al3 --discovery-token-ca-cert-hash sha256:f4e3c924918545abfd8148e71a377e086d591c6a59856a496b4b33e0987d9c18 [root@node2 ~]# kubeadm join 192.168.8.30:6443 --token yfjrsi.60lmqgzobp0b8al3 --discovery-token-ca-cert-hash sha256:f4e3c924918545abfd8148e71a377e086d591c6a59856a496b4b33e0987d9c18 8. master 노드에 Calico 네트워크 플러그인 설치 1 2 # Calico 네트워크 플러그인 설치 [root@master ~]# kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml 참고：잘 알려진 이유로 Calico 이미지 다운로드에 실패할 수 있습니다.\n해당 매니페스트 파일을 수정해 두었으니, master 노드에 다운로드하여 적용하세요：\n이 파일은 Baidu Netdisk(百度网盘)에 업로드되어 있습니다. 다운로드 링크: https://pan.baidu.com/s/1BPCFxv0kE0KWb04Qsgy71g (추출 코드: s44k)\n1 [root@master ~]# kubectl apply -f /root/calico-ucloud.yaml 설치 완료 후 다음을 실행합니다：\n1 2 3 4 5 [root@master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION master Ready control-plane 65m v1.30.13 node1 Ready \u0026lt;none\u0026gt; 62m v1.30.0 node2 Ready \u0026lt;none\u0026gt; 62m v1.30.0 위 결과가 보이면 Kubernetes 설치가 완료된 것입니다.\n9. 추가 내용 각 노드에서 다음 명령을 실행합니다：\n1 2 3 4 5 kubectl completion bash \u0026gt; /etc/bash_completion.d/kubectl source /etc/bash_completion.d/kubectl vim /root/.bashrc alias docker=\u0026#39;crictl\u0026#39; source /root/.bashrc 참고:\nkubectl 셸 완성: 위의 첫 두 줄은 kubectl 완성 스크립트를 /etc/bash_completion.d/에 기록합니다. 단, 이전에 시스템에 bash-completion 패키지가 설치되어 있어야 합니다(yum install -y bash-completion 또는 apt install -y bash-completion). 설치되어 있지 않으면 해당 디렉터리가 존재하지 않아 완성이 동작하지 않습니다. 또한 kubectl 자체도 이 두 명령을 실행해야 비로소 완성이 활성화됩니다(Kubernetes는 이 추가 설정이 필요합니다). docker 별칭: Kubernetes가 containerd 런타임으로 전환되면서 컨테이너 관리 명령은 docker 대신 crictl을 사용합니다. 여기서 docker를 crictl로 별칭하는 것은 docker 명령에 익숙한 사용자를 배려하여 기존 작업 습관을 그대로 유지할 수 있게 하기 위함입니다. # docker images를 실행했을 때 다음과 같은 오류가 발생하는 경우：\nKubernetes에 다음 경고가 표시되면 해결 방법은 다음과 같습니다.\nWARN[0000] image connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.\n이 경고는 사용 중인 컨테이너 런타임 클라이언트(예： crictl 또는 kubelet)가 기본 엔드포인트 설정을 사용하고 있으며, 이 설정은 이제 더 이상 사용되지 않으므로 명시적으로 지정해야 함을 나타냅니다. 해결 단계는 다음과 같습니다：\n1 2 3 4 5 6 7 cat \u0026gt; /etc/crictl.yaml \u0026lt;\u0026lt; EOF runtime-endpoint: unix:///run/containerd/containerd.sock # 사용하는 컨테이너 런타임에 맞게 선택 image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false pull-image-on-create: false EOF 설명：\nContainerd를 사용하는 경우 엔드포인트는 unix:///run/containerd/containerd.sock입니다.\nCRI-O를 사용하는 경우 엔드포인트는 unix:///run/crio/crio.sock입니다.\n비고：선택 사항.\nKubernetes를 수동으로 설치하면 기본적으로 데이터 전송에 iptables를 사용합니다. 대규모 환경에서는 IPVS 모드로 변경하는 것이 좋습니다. 설치 완료 후 IPVS 모드로 변경하는 단계는 다음과 같습니다：\n1. 모든 노드에 ipvsadm 패키지 설치 2. 모든 노드에서 모듈 로드 1 2 3 4 5 6 7 cat \u0026gt; /etc/modules-load.d/ipvs.conf \u0026lt;\u0026lt; EOF ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack EOF 3. kube-proxy ConfigMap 편집 1 kubectl edit cm kube-proxy -n kube-system 열린 편집기에서 다음 섹션을 찾아 수정합니다：\n1 2 3 4 5 6 7 apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: \u0026#34;ipvs\u0026#34; # \u0026#34;iptables\u0026#34;를 \u0026#34;ipvs\u0026#34;로 변경 ipvs: scheduler: \u0026#34;rr\u0026#34; # 로드 밸런싱 알고리즘： rr|wrr|lc 4. kube-proxy Pod 강제 재시작 모든 kube-proxy Pod를 삭제하여 재생성을 트리거합니다：\n1 #kubectl delete pods -n kube-system -l k8s-app=kube-proxy 5. 새 Pod가 정상 동작하는지 확인 1 2 #kubectl get pods -n kube-system -l k8s-app=kube-proxy # 모든 Pod가 Running 상태여야 합니다 6. IPVS 설정이 적용되었는지 확인 1 2 3 4 5 [root@master ~]# ipvsadm -L -n TCP 192.168.8.30:30080 rr -\u0026gt; 10.244.104.3:80 Masq 1 0 0 -\u0026gt; 10.244.104.4:80 Masq 1 0 0 -\u0026gt; 10.244.166.132:80 Masq 1 0 0 ","permalink":"https://LMDAWN1127.github.io/ko/posts/k8s-v1.30-manual-deploy/","summary":"kubeadm으로 Kubernetes v1.30 클러스터를 수동 배포하는 단계별 가이드입니다. 방화벽/SELinux, 스왑, 커널 모듈, Containerd, kubeadm init, 워커 가입, Calico, IPVS 추가까지 다룹니다.","title":"Kubernetes v1.30 클러스터 수동 배포 (kubeadm 방식)"},{"content":" 이 글은 KVM 가상화 기술을 체계적으로 소개하며, 발전 역사, 배포 방법, 가상 머신 관리, 네트워크 모드 구성 및 명령줄을 통한 자동 설치를 다룹니다. 모든 명령은 CentOS/RHEL 환경에서 검증되었습니다.\nI. KVM 발전 역사 KVM(Kernel-based Virtual Machine)은 오늘날 클라우드 컴퓨팅 분야의 주류 가상화 기술입니다. 그 발전 과정을 이해하면 업계가 왜 결국 KVM을 선택했는지 알 수 있습니다.\n가상화 기술은 크게 두 가지로 나눌 수 있습니다: 폐쇄형 가상화(예: VMware, Microsoft Hyper-V)와 오픈소스 가상화(예: Xen, KVM).\n초기 오픈소스 가상화의 대표 주자는 Xen이었습니다. AWS와 알리바바 클라우드는 초기에 모두 Xen 방식을 채택했습니다. 시트릭스의 XenServer 및 데스크톱 클라우드 제품인 FusionAccess(Citrix XenDesktop에 대응)도 Xen을 기반으로 했습니다. 화웨이의 FusionCompute도 6.3 버전 이전에는 Xen을 사용했습니다.\n주요 이정표는 다음과 같습니다:\n2008년: Red Hat이 KVM을 인수하고 RHEL 5.4에서 정식으로 KVM을 출시했습니다(RHEL 5.0에는 Xen이 탑재되었습니다). 2010년: OpenStack 프로젝트가 탄생했습니다. Red Hat은 IBM, Dell, VMware, Cisco, HP 등과 함께 KVM 오픈소스 커뮤니티를 포용하고 오픈소스 소프트웨어 재단 아래에서 생태계를 구축했습니다. 같은 시기에 시트릭스는 CloudStack 커뮤니티를 추진했습니다. 2014년: RHEL 5.11이 출시되었습니다(RHEL 5.x 시리즈의 수명 종료, 7년의 기술 지원 제공 및 KVM으로의 무료 업그레이드 가능). 2017년 전후: AWS가 KVM 기반 Nitro 플랫폼 인스턴스를 출시했고, 알리바바 클라우드와 전 세계 수많은 업체가 KVM을 전면 채택했습니다. 오늘날 주요 클라우드 서비스 제공업체의 가상화 기반은 기본적으로 KVM입니다. 화웨이의 FusionCompute 6.3도 Xen에서 KVM으로 전환했습니다. 2019년 IBM이 약 340억 달러에 Red Hat을 인수하면서 Ansible, GlusterFS, Ceph 등 Red Hat 생태계 프로젝트가 그 산하에 편입되었으며, 이는 이 기술 노선의 가치를 추가로 입증합니다.\nII. KVM이란 무엇인가 KVM은 Kernel-based Virtual Machine의 약자입니다. 핵심 특징은 다음과 같습니다:\n커널 업그레이드 불필요: 시스템 자체의 커널을 직접 재사용하며, 각 가상 머신은 하나의 프로세스로 실행됩니다. 경량 가상화 플랫폼: 기존 방식에 비해 더 가볍고 효율적입니다. 통합 관리: Xen/KVM 가상화 플랫폼 모두 통합 관리 플랫폼과 명령줄 도구를 제공합니다. III. KVM 배포 1. KVM 필수 패키지 설치 전제 조건: 물리 머신 BIOS에서 가상화 지원 VT-x를 활성화합니다(대부분의 컴퓨터에서 기본 활성화됨). VMware 내부에 중첩 설치하는 경우 VMware 설정에서도 VT-x 지원을 활성화해야 합니다.\n권장 리소스 구성: CPU 4코어, 메모리 4GB.\n1 2 [root@cloud ~]# yum -y groupinstall \u0026#34;Virtualization*\u0026#34; [root@cloud ~]# systemctl restart libvirtd.service 2. 그래픽 도구로 가상 머신 설치 이 실습에서는 그래픽 도구 virt-manager를 사용하여 가상 머신을 생성하고 설치합니다.\n참고: 그래픽 마법사의 조작 과정은 이 글에서 스크린샷으로 보여드리지 않습니다. 본문은 명령줄 조작을 위주로 하며, 그래픽 인터페이스는 안내에 따라 클릭하면 완료할 수 있습니다. 또한, \u0026ldquo;호스트 머신\u0026quot;이란 KVM 가상 머신을 실행하는 머신을 의미합니다. 이 실습은 VMware에서 중첩 가상화를 수행하므로 여기서 호스트 머신은 물리 머신이 아닌 VMware 가상 머신입니다.\n먼저 시스템 설치 이미지를 준비합니다: /isos 디렉터리를 생성하고 CentOS 8.4 ISO 파일을 복사합니다.\n1 [root@cloud ~]# mkdir /isos 그래픽 관리 인터페이스를 실행합니다:\n1 [root@cloud ~]# virt-manager 인터페이스에서 새 가상 머신 생성을 클릭하고, /isos에 넣은 ISO를 설치 소스로 선택한 후 마법사에 따라 CPU, 메모리, 디스크를 구성하면 설치가 완료됩니다. 설치된 가상 머신의 디스크 파일은 기본적으로 /var/lib/libvirt/images/ 디렉터리에 server1.qcow2와 같은 이름으로 저장됩니다:\n1 2 3 [root@cloud ~]# cd /var/lib/libvirt/images/ [root@cloud images]# ls server1.qcow2 3. 디스크 생성 이전 단계의 그래픽 설치 시 virt-manager가 자동으로 디스크를 생성합니다. 여기서는 수동 생성 방법을 소개하며, 이는 나중에 클라우드 이미지 제작 및 가상 머신에 디스크 추가 시 사용됩니다. qemu-img를 사용하여 qcow2 형식 디스크를 생성합니다:\n1 [root@cloud ~]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server1.qcow2 10g 팁: 이 방식으로 클라우드 이미지를 생성할 때 용량이 작을수록 생성 속도가 빠릅니다. 이미지를 최소한으로 만드는 방법을 연구해 볼 수 있습니다. 일반적으로 시스템 디스크는 성능 요구사항이 높지 않으며, 실제 성능 병목은 데이터 디스크에 있습니다.\n4. 브리지 모드 생성 (업링크가 있는 가상 스위치) 네트워크 구성 디렉터리로 이동하여 기존 네트워크 인터페이스를 기반으로 브리지 구성을 복사합니다:\n1 2 [root@cloud ~]# cd /etc/sysconfig/network-scripts/ [root@cloud network-scripts]# cp ifcfg-ens33 ifcfg-br0 브리지 인터페이스 ifcfg-br0 편집:\n1 [root@cloud network-scripts]# cat ifcfg-br0 1 2 3 4 5 6 7 8 TYPE=Bridge NAME=br0 DEVICE=br0 ONBOOT=yes IPADDR=192.168.8.99 PREFIX=24 GATEWAY=192.168.8.254 DNS1=192.168.8.254 물리 인터페이스 ifcfg-ens33을 br0에 브리지하도록 편집:\n1 [root@cloud network-scripts]# cat ifcfg-ens33 1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 네트워크 구성을 다시 로드하고 연결을 재시작합니다:\n1 2 3 [root@cloud ~]# nmcli connection reload [root@cloud ~]# nmcli connection down ens33 ; nmcli connection up ens33 [root@cloud ~]# nmcli connection down br0 ; nmcli connection up br0 5. 클라우드 이미지 제작 요구사항: 사용자 정의 클라우드 머신 이미지를 생성합니다.\nKVM 가상 머신 배포 완료 후, 가상 머신에 접속하여 필요한 소프트웨어를 설치합니다:\n로컬 yum 저장소 구성 필수 패키지 설치: 1 # yum -y install net-tools vim-enhanced bash-completion cloud-init /etc/cloud/cloud.cfg 구성 파일 편집, 실제 요구에 따라 매개변수 조정 /etc/ssh/ssh_host* 삭제 init 0으로 종료 마지막으로 이미지를 압축합니다:\n1 [root@cloud images]# qemu-img convert -c -O qcow2 /var/lib/libvirt/images/server1.qcow2 /isos/server1.qcow2 IV. KVM 관리 virsh 대화형 인터페이스 진입:\n1 [root@cloud ~]# virsh 주요 가상 머신 관리 명령은 다음과 같습니다.\n모든 가상 머신 나열:\n1 2 3 4 [root@cloud ~]# virsh list --all Id Name State ------------------------- - server1 shut off 가상 머신 시작 / 종료 / 강제 종료 / 재부팅:\n1 2 3 4 [root@cloud ~]# virsh start server1 [root@cloud ~]# virsh shutdown server1 [root@cloud ~]# virsh destroy server1 [root@cloud ~]# virsh reboot server1 6. 가상 머신 콘솔 인터페이스 진입 KVM 가상 머신에 진입하여 가상 머신에 가상 콘솔을 추가함으로써 호스트 머신에서 콘솔 방식으로 가상 머신에 접근할 수 있습니다.\n가상 머신 내에서 grub 구성 편집:\n1 [root@VM1 ~]# vim /etc/default/grub GRUB_CMDLINE_LINUX 끝에 console=ttyS0 추가:\n1 GRUB_CMDLINE_LINUX=\u0026#34;crashkernel=auto resume=UUID=94e88e69-fad6-4d1f-99cf-0ba83cc793b5 rhgb quiet console=ttyS0\u0026#34; grub 구성 재생성 및 재부팅:\n1 2 [root@VM1 ~]# grub2-mkconfig -o /boot/grub2/grub.cfg [root@VM1 ~]# reboot 호스트 머신에서 콘솔 연결:\n1 [root@cloud ~]# virsh console server1 콘솔 종료 단축키는 Ctrl + ]입니다.\n7. 구성을 통한 가상 머신 디스크 추가 (1) 디스크 파일 생성:\n1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/disk2.qcow2 5g (2) 구성 파일로 디스크 추가:\n1 [root@cloud ~]# vim /etc/libvirt/qemu/server1.xml 구성에 다음 disk 섹션 추가:\n1 2 3 4 5 6 \u0026lt;disk type=\u0026#39;file\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;qcow2\u0026#39;/\u0026gt; \u0026lt;source file=\u0026#39;/var/lib/libvirt/images/disk2.qcow2\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vdb\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x07\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; 서비스 재시작 및 가상 머신 시작:\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server1 [root@cloud ~]# virsh console server1 KVM 가상 머신 내에서 디스크 확인:\n1 2 3 [root@localhost ~]# fdisk -l /dev/vda /dev/vdb 비교: Xen 가상화에서 디스크 장치명은 /dev/xvda, /dev/xvdb입니다.\n8. KVM 가상 머신 빠른 생성 기존 가상 머신의 XML 구성을 복사하여 빠르게 생성:\n1 2 3 4 [root@cloud qemu]# cp server1.xml server2.xml [root@cloud qemu]# uuidgen f3ccabc3-ca4c-41f2-8d6f-e99077c24eeb [root@cloud qemu]# vim server2.xml name, uuid, 디스크 경로 및 MAC 주소 등 주요 매개변수 수정:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 \u0026lt;domain type=\u0026#39;kvm\u0026#39;\u0026gt; \u0026lt;name\u0026gt;server2\u0026lt;/name\u0026gt; \u0026lt;uuid\u0026gt;f3ccabc3-ca4c-41f2-8d6f-e99077c24eeb\u0026lt;/uuid\u0026gt; \u0026lt;metadata\u0026gt; \u0026lt;disk type=\u0026#39;file\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;qcow2\u0026#39;/\u0026gt; \u0026lt;source file=\u0026#39;/isos/server1.qcow2\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vda\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x04\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; \u0026lt;interface type=\u0026#39;bridge\u0026#39;\u0026gt; \u0026lt;mac address=\u0026#39;52:54:00:96:f6:a2\u0026#39;/\u0026gt; \u0026lt;source bridge=\u0026#39;br0\u0026#39;/\u0026gt; \u0026lt;model type=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x01\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/interface\u0026gt; 서비스 재시작 및 새 가상 머신 시작:\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server2 [root@cloud ~]# virsh list --all V. KVM 네트워크 구성 KVM에는 일반적으로 세 가지 네트워크 모드가 있습니다: NAT 모드, 호스트 전용 모드(Host-Only), 브리지 모드.\n1. NAT 모드 iptables 주소 변환(MASQUERADE)을 통해 NAT를 구현합니다:\n1 # iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE 또는 SNAT으로 소스 주소를 지정:\n1 # iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j SNAT --to-source 192.168.8.99 NAT 규칙 확인:\n1 [root@cloud ~]# iptables -t nat -nL 기본 네트워크 구성 파일은 /etc/libvirt/qemu/networks에 위치:\n1 2 3 [root@cloud networks]# pwd /etc/libvirt/qemu/networks [root@cloud networks]# vim default.xml 2. 호스트 전용 모드 호스트 전용 모드에서는 가상 머신이 호스트 머신과만 통신할 수 있으며 외부 네트워크에 접근할 수 없습니다.\n3. KVM 가상 머신에서 인터넷 접근 허용 (1) VMware 호스트 머신에 인터넷 접근이 가능한 네트워크 인터페이스를 추가합니다.\n(2) 호스트 머신 운영체제에 진입하여 이중 브리지를 구성합니다.\nifcfg-br0(내부망 브리지):\n1 2 3 4 5 6 TYPE=Bridge NAME=br0 DEVICE=br0 ONBOOT=yes IPADDR=192.168.8.99 PREFIX=24 ifcfg-br1(외부망 브리지, DHCP):\n1 2 3 4 5 TYPE=Bridge NAME=br1 DEVICE=br1 ONBOOT=yes BOOTPROTO=dhcp ifcfg-ens33을 br0에 브리지:\n1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 ifcfg-ens37을 br1에 브리지:\n1 2 3 4 5 TYPE=Ethernet NAME=ens37 DEVICE=ens37 ONBOOT=yes BRIDGE=br1 서비스 재시작 및 가상 머신 종료:\n1 2 3 4 5 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh shutdown server1 Domain server1 is being shutdown [root@cloud ~]# virsh shutdown server2 Domain server2 is being shutdown KVM 내 server1 가상 머신의 네트워크 인터페이스를 br1에 브리지하도록 수정하고, VM1 내에서 IP 획득 방식을 DHCP로 변경:\n1 2 3 4 5 6 7 8 9 10 11 [root@VM1 ~]# nmcli connection modify enp1s0 ipv4.method auto autoconnect yes [root@VM1 ~]# nmcli connection down enp1s0 ; nmcli connection up enp1s0 [root@VM1 ~]# ifconfig enp1s0: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1500 inet 192.168.31.158 netmask 255.255.255.0 broadcast 192.168.31.255 inet6 fe80::5054:ff:fe96:f6a1 prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether 52:54:00:96:f6:a1 txqueuelen 1000 (Ethernet) RX packets 1253 bytes 279527 (272.9 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 119 bytes 17932 (17.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 외부 네트워크 연결 테스트:\n1 2 3 4 5 [root@VM1 ~]# ping www.baidu.com PING www.a.shifen.com (183.2.172.17) 56(84) bytes of data. 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=1 ttl=52 time=23.6 ms 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=2 ttl=52 time=24.3 ms 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=3 ttl=52 time=23.5 ms VI. 명령줄로 KVM 가상 머신 설치 kickstart 자동 설치 스크립트를 활용하여 무인 방식으로 가상 머신을 일괄 설치할 수 있습니다.\n1. 디스크 생성 1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server3.qcow2 10g 2. 자동 설치 virt-install로 설치 소스와 kickstart 파일을 지정:\n1 2 3 4 5 [root@cloud ~]# virt-install -n server3 --memory 2048 --vcpu 2 \\ --disk /var/lib/libvirt/images/server3.qcow2 \\ --network bridge=br1 \\ -l http://192.168.31.96/pub \\ -x inst.ks=http://192.168.31.96/ks/ks01.cfg --vnc kickstart 구성 파일 ks01.cfg 예시:\n1 # vim /var/www/html/ks/ks01.cfg 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #version=RHEL8 # Use text install for fully unattended text repo --name=\u0026#34;AppStream\u0026#34; --baseurl=http://192.168.0.107/pub/AppStream repo --name=\u0026#34;BaseOS\u0026#34; --baseurl=http://192.168.0.107/pub/BaseOS reboot %packages @^minimal-environment kexec-tools %end # Keyboard layouts keyboard --xlayouts=\u0026#39;us\u0026#39; # System language lang en_US.UTF-8 # Network information network --bootproto=dhcp --activate --hostname=server3 --onboot=yes # Use network installation media url --url=http://192.168.0.107/pub/BaseOS # Run the Setup Agent on first boot firstboot --disable ignoredisk --only-use=vda clearpart --all --initlabel autopart # System timezone timezone Asia/Shanghai --utc # Root password rootpw --iscrypted $6$c31XqEglO8ECIli0$enF8T.LW3OknT.178Vb9m2HHYD6dqRdKF2QZia5.G5Tv/zMD//04S2h61oBOKihJZwPIUmWxLc7oXuWtkHQy10 user --name=admin --password=$6$Ey847S5bcqYg8ktk$sd4BJeFF01IFsGOaFwRoqXoYuZ5H1RV3bLC6n6gQGa1tNAjplFDhTnm3Cjw2pgwltIE.xr27X7E/ro4gF2JSG1 --iscrypted --gecos=\u0026#34;admin\u0026#34; %addon com_redhat_kdump --enable --reserve-mb=\u0026#39;auto\u0026#39; %end %anaconda pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty %end 참고: 위 설치 소스 주소(192.168.0.107), 호스트명(server3) 및 rootpw, user 뒤의 비밀번호 해시는 예시입니다. 실제 사용 시 자신의 환경 주소와 생성한 비밀번호 암호문으로 교체하십시오.\n","permalink":"https://LMDAWN1127.github.io/ko/posts/kvm-virtualization-guide/","summary":"KVM 가상화 기술을 체계적으로 소개합니다. 발전 역사, 배포 방법, 가상 머신 관리, 네트워크 모드 구성 및 명령줄을 통한 가상 머신 자동 설치를 다룹니다.","title":"KVM 가상화 입문부터 실전까지: 배포, 관리 및 네트워크 구성"},{"content":"1. 적절한 베이스 이미지 선택 1 2 3 4 5 6 7 8 9 10 # ❌ 너무 큼 FROM ubuntu:22.04 # ~77MB FROM python:3.11 # ~1GB # ✅ 추천 FROM python:3.11-alpine # ~50MB FROM python:3.11-slim # ~130MB # ✅ 최소 크기 FROM gcr.io/distroless/python3 # ~20MB 베이스 이미지 크기 ubuntu:22.04 ~77MB python:3.11 ~1GB python:3.11-alpine ~50MB distroless ~20MB 2. 멀티스테이지 빌드 사용 가장 효과적인 최적화 방법: 컴파일 스테이지에서는 큰 이미지를 사용하고 실행 스테이지에서는 산출물만 복사합니다.\n1 2 3 4 5 6 7 8 9 10 11 # 스테이지 1: 컴파일 FROM golang:1.21 AS builder WORKDIR /app COPY . . RUN go build -o myapp # 스테이지 2: 실행 FROM alpine:3.18 WORKDIR /app COPY --from=builder /app/myapp . CMD [\u0026#34;./myapp\u0026#34;] Go 애플리케이션이 ~1GB에서 ~15MB로 축소됩니다.\n3. RUN 명령어 병합 1 2 3 4 5 6 7 8 9 10 11 12 # ❌ 여러 레이어 RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y wget RUN rm -rf /var/lib/apt/lists/* # ✅ 하나의 레이어로 병합 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends \\ curl \\ wget \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* 4. 빌드 캐시 정리 1 2 3 4 5 6 7 8 9 # Python - pip 캐시 비활성화 RUN pip install --no-cache-dir -r requirements.txt # Node.js - npm 캐시 정리 RUN npm ci --only=production \u0026amp;\u0026amp; \\ npm cache clean --force # Go - 디버그 정보 제거 RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 5. .dockerignore 사용 1 2 3 4 5 6 7 8 9 10 11 # .dockerignore .git node_modules *.md .env .vscode __pycache__ *.pyc dist build coverage 1 2 # 컨텍스트 크기 확인 docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; 6. 필요한 패키지만 설치 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # ❌ 실행 이미지에 빌드 도구가 포함됨 FROM ubuntu:22.04 RUN apt-get install -y build-essential gcc make cmake COPY . . RUN make \u0026amp;\u0026amp; make install CMD [\u0026#34;./myapp\u0026#34;] # ✅ 빌드와 실행 분리 FROM ubuntu:22.04 AS builder RUN apt-get install -y build-essential gcc make COPY . . RUN make \u0026amp;\u0026amp; make install FROM ubuntu:22.04 COPY --from=builder /app/myapp . CMD [\u0026#34;./myapp\u0026#34;] 7. \u0026ndash;no-install-recommends 사용 1 2 3 4 5 # ❌ 모든 추천 패키지 설치 RUN apt-get install -y curl # ✅ 필수 패키지만 설치 (의존성 약 50% 감소) RUN apt-get install -y --no-install-recommends curl 8. 정적 리소스 압축 1 2 3 4 5 6 7 8 9 10 11 # JavaScript 압축 RUN uglifyjs app.js -o app.min.js # CSS 압축 RUN cssnano app.css app.min.css # 이미지 압축 RUN find . -name \u0026#34;*.png\u0026#34; -exec optipng {} \\; # Go 바이너리 디버그 정보 제거 RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 9. Squash로 레이어 병합 1 2 # 모든 레이어를 하나로 병합하여 중간 레이어의 중복 파일 제거 docker build --squash -t myapp:slim . /etc/docker/daemon.json에서 experimental 기능을 활성화해야 합니다.\n10. 이미지 레이어 분석 1 2 3 4 5 6 7 # dive 도구로 각 레이어의 크기와 내용 분석 docker run --rm -it \\ -v /var/run/docker.sock:/var/run/docker.sock \\ wagoodman/dive myapp:latest # 또는 docker history 사용 docker history myapp:latest 실전 사례: Python 애플리케이션 1 2 3 4 5 6 # ❌ 최적화 전 (1.2GB) FROM python:3.11 WORKDIR /app COPY . . RUN pip install -r requirements.txt CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 최적화 후 (150MB, 87% 감소) FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir --user -r requirements.txt FROM python:3.11-slim WORKDIR /app COPY --from=builder /root/.local /root/.local COPY . . ENV PATH=/root/.local/bin:$PATH CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 실전 사례: Node.js 애플리케이션 1 2 3 4 5 6 # ❌ 최적화 전 (1.1GB) FROM node:18 WORKDIR /app COPY . . RUN npm install CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 최적화 후 (180MB, 84% 감소) FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production FROM node:18-alpine WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY . . USER node CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 추천 도구 도구 용도 dive 이미지의 각 레이어 내용과 크기 분석 docker-slim 이미지 자동 최적화 hadolint Dockerfile 정적 검사 trivy 이미지 보안 스캔 ","permalink":"https://LMDAWN1127.github.io/ko/posts/dockerfile-smaller-image/","summary":"Docker 이미지가 너무 커서 배포가 느리고 스토리지 비용이 부담되시나요? 이 글에서는 이미지 크기를 90% 이상 줄일 수 있는 검증된 10가지 최적화 팁을 공유합니다.","title":"Dockerfile 이미지가 너무 크다고요? 이미지 크기를 90% 줄이는 10가지 팁"},{"content":"1. 명령어 순서 최적화 (캐시 활용) 변경 빈도가 낮은 명령어를 앞에, 높은 명령어를 뒤에 배치하세요. 특정 레이어의 캐시가 무효화되면 그 이후의 모든 레이���가 다시 빌드됩니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ 잘못된 예: 코드 수정 시마다 의존성을 다시 설치 FROM node:18-alpine WORKDIR /app COPY . . # 코드는 자주 변경됨 RUN npm ci --production # 의존성은 거의 변경되지 않음 CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # ✅ 올바른 예: 의존성 변경 시에만 다시 설치 FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # 의존성 정의 (거의 변경되지 않음) RUN npm ci --production # 의존성 설치 (거의 변경되지 않음) COPY . . # 코드 (자주 변경됨) CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 # 캐시 적중 여부 확인 docker build -t myapp . 2\u0026gt;\u0026amp;1 | grep -E \u0026#34;(CACHED|Using cache)\u0026#34; 2. .dockerignore로 컨텍스트 축소 docker build는 .git, node_modules 등 불필요한 파일을 포함한 전체 디렉터리를 Docker daemon으로 전송합니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # .dockerignore .git .github .vscode .idea node_modules dist build *.md *.log .env .env.local .DS_Store coverage .nyc_output 1 2 3 4 # 컨텍스트 크기 확인 docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; # 최적화 전: Sending build context to Docker daemon 1.2GB # 최적화 후: Sending build context to Docker daemon 50MB 3. 경량 베이스 이미지 사용 베이스 이미지 크기 설명 ubuntu:22.04 ~77MB 완전한 Ubuntu python:3.11 ~1GB 완전한 Python 포함 python:3.11-slim ~130MB 경량 버전 python:3.11-alpine ~50MB Alpine 버전 1 2 3 4 5 6 7 8 # ❌ 너무 큼 FROM python:3.11 # 1GB # ✅ 추천 FROM python:3.11-slim # 130MB # ✅ 최소 크기 (musl 호환성 문제 주의) FROM python:3.11-alpine # 50MB Alpine은 glibc 대신 musl을 사용하므로 일부 소프트웨어가 호환되지 않을 수 있습니다. 문제가 발생하면 slim 버전을 선택하세요.\n4. RUN 명령어 병합으로 레이어 수 감소 각 RUN은 새로운 레이어를 생성하며, 레이어가 많을수록 빌드가 느려지고 이미지가 커집니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ 5개 레이어 RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y wget RUN apt-get clean RUN rm -rf /var/lib/apt/lists/* # ✅ 1개 레이어 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends \\ curl \\ wget \u0026amp;\u0026amp; \\ apt-get clean \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* 1 2 # 이미지 레이어 수 확인 docker history myapp:latest 5. 멀티스테이지 빌드 사용 한 스테이지에서 빌드하고 다른 스테이지에서 실행하여 필요한 파일만 복사합니다.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 스테이지 1: 빌드 FROM golang:1.21-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o app . # 스테이지 2: 실행 (~15MB) FROM alpine:3.18 RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/app . EXPOSE 8080 CMD [\u0026#34;./app\u0026#34;] 방식 이미지 크기 빌드 시간 싱글 스테이지 ~1.2GB 5분 멀티 스테이지 ~15MB 2분 6. BuildKit으로 빌드 가속화 BuildKit은 병렬 빌드와 고급 캐싱을 지원합니다.\n1 2 3 4 5 6 7 # BuildKit 활성화 export DOCKER_BUILDKIT=1 # 또는 /etc/docker/daemon.json에 영구 설정 # { # \u0026#34;features\u0026#34;: { \u0026#34;buildkit\u0026#34;: true } # } 1 2 3 4 5 6 7 8 9 10 # 캐시 마운트 사용 (BuildKit 기능) # syntax=docker/dockerfile:1 FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # 캐시 마운트로 매번 의존성을 다시 다운로드하지 않음 RUN --mount=type=cache,target=/root/.npm \\ npm ci --production COPY . . CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 # BuildKit 활성화 여부 확인 docker build --progress=plain -t myapp . 2\u0026gt;\u0026amp;1 | head -5 7. 캐시 가져오기/내보내기 사용 CI/CD 환경에서는 매 빌드 후 캐시가 손실됩니다. 캐시 가져오기/내보내기를 사용하면 빌드 간 캐시를 유지할 수 있습니다.\n1 2 3 4 5 6 7 8 9 10 11 # 로컬로 내보내기 docker build \\ --cache-from type=local,src=/tmp/cache \\ --cache-to type=local,dest=/tmp/cache \\ -t myapp . # 레지스트리를 캐시로 사용 docker build \\ --cache-from type=registry,ref=myregistry/myapp:cache \\ --cache-to type=registry,ref=myregistry/myapp:cache,mode=max \\ -t myapp . 1 2 3 4 5 6 7 8 9 # GitHub Actions 예제 - name: Build and push uses: docker/build-push-action@v5 with: context: . push: true tags: myapp:latest cache-from: type=gha cache-to: type=gha,mode=max 8. 의존성 병렬 설치 1 2 3 4 5 6 7 8 9 10 11 12 # Node.js - npm ci가 npm install보다 빠름 RUN npm ci --production # Python - 캐시 비활성화 RUN pip install --no-cache-dir -r requirements.txt # Go - 모듈 병렬 다운로드 RUN go mod download # Rust - cargo-chef 사용 COPY --from=chef /app/recipe.json recipe.json RUN cargo chef cook --release --recipe-path recipe.json 9. 빌드 캐시 및 임시 파일 정리 동일한 RUN 명령어 내에서 정리하여 파일이 레이어에 남지 않도록 합니다.\n1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 올바른 예: 동일 레이어에서 정리 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends build-essential \u0026amp;\u0026amp; \\ make \u0026amp;\u0026amp; make install \u0026amp;\u0026amp; \\ apt-get purge -y --auto-remove build-essential \u0026amp;\u0026amp; \\ apt-get clean \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # ❌ 잘못된 예: 정리가 다른 레이어에서 수행되어 파일이 여전히 존재함 RUN apt-get update \u0026amp;\u0026amp; apt-get install -y build-essential RUN make \u0026amp;\u0026amp; make install RUN apt-get clean # 너무 늦었습니다! 이��� 레이어에 파일이 이미 포함됨 언어별 정리 명령어:\n1 2 3 4 5 6 7 8 # Node.js RUN npm ci --production \u0026amp;\u0026amp; npm cache clean --force # Python RUN pip install --no-cache-dir -r requirements.txt # Go RUN go build -o app . \u0026amp;\u0026amp; go clean -cache 10. 특정 버전 태그 사용 1 2 3 4 5 6 7 8 # ❌ latest는 변경될 수 있어 캐시를 예측할 수 없음 FROM node:latest # ✅ 특정 버전 FROM node:18.17.0-alpine3.18 # ✅ 더 안전: SHA256 다이제스트 사용 FROM node:18.17.0-alpine3.18@sha256:abc123... 이미지 레이어 상세 설명 1 2 # 이미지 레이어 히스토리 확인 docker history \u0026lt;image-name\u0026gt; 새 레이어를 생성하는 명령어: FROM, RUN, COPY, ADD\n레이어를 생성하지 않는 명령어 (메타데이터만): CMD, ENTRYPOINT, ENV, EXPOSE, WORKDIR, USER, LABEL, ARG, VOLUME\n1 2 3 4 5 6 7 8 9 10 11 # 4개 레이어 생성 FROM node:18-alpine # 레이어 1 RUN apk add --no-cache curl # 레이어 2 COPY package.json . # 레이어 3 RUN npm install # 레이어 4 # 이 명령어들은 레이어를 추가하지 않음 WORKDIR /app # 레이어 없음 ENV NODE_ENV=production # 레이어 없음 EXPOSE 3000 # 레이어 없음 CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # 레이어 없음 최적화 효과 비교 1 2 3 4 5 6 7 8 9 # ❌ 최적화 전 (8분, 1.2GB) FROM ubuntu:22.04 RUN apt-get update RUN apt-get install -y python3 python3-pip RUN apt-get install -y build-essential WORKDIR /app COPY . . RUN pip3 install -r requirements.txt CMD [\u0026#34;python3\u0026#34;, \u0026#34;app.py\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ✅ 최적화 후 (1.5분, 150MB) # syntax=docker/dockerfile:1 FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN --mount=type=cache,target=/root/.cache/pip \\ pip install --no-cache-dir -r requirements.txt FROM python:3.11-slim WORKDIR /app COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY . . EXPOSE 8000 CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 지표 최적화 전 최적화 후 개선 빌드 시간 8분 1.5분 81% ⬇️ 이미지 크기 1.2GB 150MB 87% ⬇️ 컨텍스트 크기 500MB 50MB 90% ⬇️ 캐시 적중률 10% 90% 80% ⬆️ 모니터링 및 디버깅 1 2 3 4 5 6 7 8 9 10 11 # 상세 빌드 시간 표시 time docker build -t myapp . # BuildKit 상세 진행 상황 DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp . # 이미지 레이어 분석 docker history myapp:latest # dive 도구로 심층 분석 dive myapp:latest ","permalink":"https://LMDAWN1127.github.io/ko/posts/dockerfile-faster-build/","summary":"Docker 빌드가 너무 느리신가요? 이 글에서는 캐시 전략부터 멀티스테이지 빌드까지, 빌드 시간을 대폭 단축할 수 있는 검증된 10가지 최적화 팁을 공유합니다.","title":"Dockerfile 빌드 속도를 높이는 방법? 10가지 실용적인 최적화 팁"},{"content":"1. 기본 문법 두 명령어의 문법은 거의 동일하지만 동작 방식에 큰 차이가 있습니다:\n1 2 3 4 5 # COPY - 순수한 파일 복사, 추가 처리를 하지 않음 COPY [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; # ADD - 자동 압축 해제 및 원격 URL을 지원하지만 예측 불가능한 동작 가능 ADD [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; 2. 압축 파일 자동 해제 ADD는 로컬 압축 파일을 자동으로 해제하지만 COPY는 그렇지 않습니다:\n1 2 3 4 5 6 7 # ADD는 .tar.gz를 자동 해제 ADD app.tar.gz /opt/app/ # 결과: /opt/app/ 아래에 압축이 해제된 내용 # COPY는 파일 자체만 복사 COPY app.tar.gz /opt/app/ # 결과: /opt/app/app.tar.gz는 원본 압축 파일 지원하는 압축 형식: .tar, .tar.gz/.tgz, .tar.bz2, .tar.xz\n압축 파일을 그대로 복사하고 해제하지 않으려면 반드시 COPY를 사용해야 합니다.\n3. 원격 URL 다운로드 ADD는 URL에서 직접 파일을 다운로드할 수 있지만 COPY는 지원하지 않습니다:\n1 2 3 4 5 # ADD는 원격 파일 다운로드 가능 (하지만 권장하지 않음) ADD https://example.com/app.tar.gz /opt/ # COPY는 URL을 지원하지 않음 COPY https://example.com/app.tar.gz /opt/ # 오류 발생! ADD로 원격 파일을 다운로드하는 대신 RUN + curl을 사용하는 것을 추천합니다. ADD 다운로드는 빌드 캐시를 활용할 수 없고, 오류 처리가 불가능하며, 무결성 검증도 할 수 없기 때문입니다.\n1 2 3 4 # 추천: RUN + curl RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \\ \u0026amp;\u0026amp; tar -xzf /tmp/app.tar.gz -C /opt/ \\ \u0026amp;\u0026amp; rm /tmp/app.tar.gz 4. 디렉터리 자동 생성 두 명령어 모두 존재하지 않는 중간 디렉터리를 자동으로 생성합니다:\n1 2 3 4 5 6 # /etc/myapp/config/가 존재하지 않으면 자동 생성 COPY app.conf /etc/myapp/config/ ADD app.conf /etc/myapp/config/ # 수동으로 mkdir 불필요 # RUN mkdir -p /etc/myapp/config ← 불필요 5. 빌드 캐시 동작 1 2 3 4 5 # COPY - 파일 내용을 엄격하게 비교, 내용이 변경되어야 캐시 무효화 COPY config/app.conf /etc/app/ # ADD - 압축 파일 내용이 동일하면 캐시 유지 ADD app.tar.gz /opt/app/ 상황 COPY ADD 파일 내용 변경 캐��� 무효화 캐시 무효화 파일 메타데이터 변경 캐시 무효화 캐시 무효화 압축 파일 내용 동일 N/A 캐시 유지 6. 기능 비교 기능 COPY ADD 로컬 파일 복사 ✅ ✅ 디렉터리 복사 ✅ ✅ 와일드카드 지원 ✅ ✅ 디렉터리 자동 생성 ✅ ✅ 압축 파일 자동 해제 ❌ ✅ 원격 URL 지원 ❌ ✅ 의미 명확성 ✅ 높음 ❌ 낮음 사용 권장 ✅ 우선 사용 ⚠️ 특정 상황 7. 사용 사례 COPY에 적합한 경우:\n1 2 3 4 5 6 7 8 9 # 설정 파일 복사 COPY nginx.conf /etc/nginx/nginx.conf # 애플리케이션 코드 복사 COPY src/ /app/src/ COPY package.json /app/ # 멀티스테이지 빌드에서 바이너리 복사 COPY --from=builder /app/build/app /usr/local/bin/ ADD는 자동 압축 해제가 필요한 경우에만 사용:\n1 2 3 4 5 # ADD의 적절한 사용 ADD node-v18.17.0-linux-x64.tar.gz /usr/local/ # 일�� 파일에는 ADD를 사용하지 마세요 ADD app.conf /etc/app/ # ← COPY로 대체 8. 모범 사례 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # 규칙 1: 기본적으로 COPY 사용 COPY requirements.txt /app/ # ✅ ADD requirements.txt /app/ # ❌ 불필요 # 규칙 2: 압축 해제가 필요할 때만 ADD 사용 ADD node-v18.tar.gz /usr/local/ # ✅ 압축 해제 필요 # 규칙 3: 원격 파일은 RUN + curl 사용 RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \\ \u0026amp;\u0026amp; tar -xzf /tmp/app.tar.gz -C /opt/ \\ \u0026amp;\u0026amp; rm /tmp/app.tar.gz # ✅ ADD https://example.com/app.tar.gz /opt/ # ❌ # 규칙 4: .dockerignore로 컨텍스트 축소 # .dockerignore # .git # node_modules # *.md # .env # 규칙 5: COPY 순서를 적절히 배치하여 캐시 활용 COPY package.json package-lock.json ./ # 의존성 먼저 복사 (변경 적음) RUN npm ci --production COPY . . # 코드 나중에 복사 (변경 많음) 9. 흔한 함정 1 2 3 4 5 6 7 8 9 10 # 함정 1: 의도치 않은 자동 압축 해제 ADD app.tar.gz /opt/app/ # 파일을 복사했다고 생각했지만 실제로는 디렉터리로 해제됨 # 함정 2: 원격 파일 캐시 불가 ADD https://github.com/user/repo/releases/download/v1.0/app.tar.gz /tmp/ # 매 빌드마다 다시 다운로드 # 함정 3: 권한 문제 COPY --chown=app:app src/ /app/src/ # 소유자 지정 10. 선택 결정 1 2 3 4 5 6 7 파일 복사가 필요한가? ├── 로컬 압축 파일이며 해제가 필요한가? │ └── ADD 사용 ├── 로컬 일반 파일인가? │ └── COPY 사용 └── 원격 파일인가? └── RUN + curl 사용 ","permalink":"https://LMDAWN1127.github.io/ko/posts/dockerfile-copy-vs-add/","summary":"COPY와 ADD 모두 파일을 복사할 수 있지만 근본적인 차이가 있습니다. 이 글에서는 두 명령어의 차이점을 상세히 분석하여 더 나은 Dockerfile을 작성할 수 있도록 도와드립니다.","title":"Dockerfile에서 COPY와 ADD의 차이점 완벽 정리"},{"content":"1. 환경 요구사항 운영체제: CentOS 7/8/Stream 9 Node.js: v18 이상 메모리: 2GB 이상 권장 네트워크: Anthropic API 접근 가능해야 함 2. Node.js 설치 1 2 3 4 5 6 7 8 9 # NodeSource 저장소 설치 curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash - # Node.js 설치 sudo yum install -y nodejs # 확인 node -v npm -v Node.js 버전이 너무 낮으면 nvm으로 버전을 관리하세요:\n1 2 3 4 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash source ~/.bashrc nvm install 20 nvm use 20 3. Claude Code 설치 1 2 3 4 5 # 전역 설치 npm install -g @anthropic-ai/claude-code # 확인 claude --version 권한 문제가 발생하면 두 가지 해결 방법이 있습니다:\n1 2 3 4 5 6 7 8 # 방법 1: sudo sudo npm install -g @anthropic-ai/claude-code # 방법 2: npm 전역 디렉터리 변경 (추천) mkdir -p ~/.npm-global npm config set prefix \u0026#34;~/.npm-global\u0026#34; echo \u0026#39;export PATH=~/.npm-global/bin:$PATH\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc 4. API Key 설정 1 2 3 4 5 6 # 방법 1: 환경 변수 (추천) echo \u0026#39;export ANTHROPIC_API_KEY=\u0026#34;your-api-key-here\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc # 확인 echo $ANTHROPIC_API_KEY 방법 2: claude를 직접 실행하면 최초 실행 시 API Key 입력을 요청합니다.\n5. 기본 사용법 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 대화형 세션 시작 cd /path/to/your/project claude # 직접 질문하기 (비대화형 모드) claude -p \u0026#34;현재 디렉터리의 모든 파일 개수를 세는 Python 스크립트를 작성해줘\u0026#34; # 코드 분석 cat main.py | claude -p \u0026#34;이 코드의 잠재적 문제점을 분석해줘\u0026#34; # 설정 확인/수정 claude config # 최신 버전으로 업데이트 claude update 명령어 설명 claude 대화형 세션 시작 claude -p \u0026quot;질문\u0026quot; 직접 질문, 비대화형 모드 claude config 설정 확인/수정 claude update 최신 버전으로 업데이트 6. 네트워크 문제 해결 Anthropic API 연결이 타임아웃되는 경우:\n1 2 3 4 5 6 # 프록시 설정 export https_proxy=\u0026#34;http://your-proxy:port\u0026#34; export http_proxy=\u0026#34;http://your-proxy:port\u0026#34; # 연결 테스트 curl -s https://api.anthropic.com/ | head -5 7. 보안 권장사항 1 2 3 4 5 6 7 8 9 # API Key를 하드코딩하지 말고 항상 환경 변수를 사용하세요 echo \u0026#39;export ANTHROPIC_API_KEY=\u0026#34;sk-ant-xxx\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.bashrc # ✅ # ANTHROPIC_API_KEY=sk-ant-xxx 코드에 직접 작성 # ❌ # 파일 권한 제한 chmod 600 ~/.bashrc # 유출 여부 확인 grep -r \u0026#34;ANTHROPIC_API_KEY\u0026#34; --include=\u0026#34;*.py\u0026#34; --include=\u0026#34;*.js\u0026#34; . ","permalink":"https://LMDAWN1127.github.io/ko/posts/centos-deploy-claude-code/","summary":"Claude Code는 Anthropic이 출시한 CLI 도구로, 터미널에서 직접 Claude AI와 상호작용하여 코딩 작업을 완료할 수 있습니다. 이 글에서는 CentOS에서의 배포 전체 과정을 소개합니다.","title":"CentOS에서 Claude Code 배포 전체 가이드"},{"content":"flannel 실전: Docker 크로스 호스트 컨테이너 네트워크 구축 1. 네트워크 환경 계획 etcd 호스트 IP: 192.168.8.88 호스트명: docker-network Host1 IP: 192.168.8.188 호스트명: web01 Host2 IP: 192.168.8.99 호스트명: web02 2. etcd 호스트 구성 단계 1: etcd 다운로드 및 압축 해제 github.com에서 etcd-v3.6.0-linux-amd64.tar.gz 소프트웨어 패키지를 다운로드합니다 소프트웨어 패키지를 etcd 호스트의 /root 디렉토리에 복사하고 압축을 해제합니다: 1 [root@docker-network ~]# tar -zxvf etcd-v3.6.0-linux-amd64.tar.gz 단계 2: etcd 실행 파일 설치 1 2 [root@docker-network ~]# cd etcd-v3.6.0-linux-amd64 [root@docker-network etcd-v3.6.0-linux-amd64]# cp etcd* /usr/local/bin/ 단계 3: 데이터 디렉토리 생성 1 [root@docker-network ~]# mkdir /var/lib/etcd 단계 4: etcd 서비스 구성 및 시작 etcd.service 파일을 생성합니다: 1 [root@docker-network ~]# vim /usr/lib/systemd/system/etcd.service 1 2 3 4 5 6 [Unit] Description=etcd_service [Service] ExecStart=/usr/local/bin/etcd --name etcd1 --data-dir /var/lib/etcd --listen-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379 --advertise-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379 [Install] WantedBy=multi-user.target 시스템 서비스 구성을 다시 로드하고 etcd를 시작합니다: 1 2 3 [root@docker-network ~]# systemctl daemon-reload [root@docker-network ~]# systemctl enable etcd.service [root@docker-network ~]# systemctl restart etcd.service etcd 리스닝 상태를 확인합니다: 1 2 3 [root@docker-network ~]# netstat -tulnp |grep :2379 tcp 0 0 192.168.8.88:2379 0.0.0.0:* LISTEN 1912/etcd tcp 0 0 127.0.0.1:2379 0.0.0.0:* LISTEN 1912/etcd 단계 5: etcd에 flannel 네트워크 구성 추가 1 etcdctl --endpoints http://127.0.0.1:2379 put /coreos.com/network/config \u0026#39;{\u0026#34;Network\u0026#34;: \u0026#34;172.16.0.0/16\u0026#34;, \u0026#34;SubnetLen\u0026#34;: 24, \u0026#34;SubnetMin\u0026#34;: \u0026#34;172.16.1.0\u0026#34;,\u0026#34;SubnetMax\u0026#34;: \u0026#34;172.16.10.0\u0026#34;, \u0026#34;Backend\u0026#34;: {\u0026#34;Type\u0026#34;: \u0026#34;vxlan\u0026#34;}}\u0026#39; 1 etcdctl --endpoints http://192.168.8.88:2379 get /coreos.com/network/config 구성 설명:\nNetwork: flannel 주소 풀 SubnetLen: 단일 호스트에 할당되는 docker0 서브넷 마스크 길이 (24비트는 255.255.255.0) SubnetMin/SubnetMax: 할당 가능한 서브넷 범위 (예: 172.16.1.0/24 ~ 172.16.10.0/24) Backend: 데이터 전달 모드 (vxlan은 호스트 간 통신 지원) 단계 6: 방화벽 비활성화 1 2 [root@docker-network ~]# systemctl disable firewalld [root@docker-network ~]# systemctl stop firewalld 3. web01 호스트 구성 (Host1: 192.168.8.188) 단계 1: flannel 다운로드 및 압축 해제 flannel-v0.26.7-linux-amd64.tar.gz를 호스트에 다운로드합니다 파일의 압축을 해제합니다: 1 [root@web01 ~]# tar -zxvf flannel-v0.26.7-linux-amd64.tar.gz 단계 2: flannel 실행 파일 설치 1 [root@web01 ~]# cp -p flanneld mk-docker-opts.sh /usr/local/bin/ 단계 3: flannel 서비스 구성 및 시작 flanneld.service 파일을 생성합니다: 1 [root@web01 ~]# vim /usr/lib/systemd/system/flanneld.service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Unit] Description=Flanneld Documentation=https://github.com/coreos/flannel After=network.target Before=docker.service [Service] User=root ExecStartPost=/usr/local/bin/mk-docker-opts.sh ExecStart=/usr/local/bin/flanneld --etcd-endpoints=http://192.168.8.88:2379 --iface=192.168.8.188 --ip-masq=true --etcd-prefix=/coreos.com/network Restart=on-failure Type=notify LimitNOFILE=65536 [Install] WantedBy=multi-user.target 매개변수 설명:\n--iface=192.168.8.188: etcd에 연결하는 로컬 IP를 지정합니다 --etcd-endpoints: etcd 서버 주소 서비스 구성을 다시 로드하고 flannel을 시작합니다:\n1 2 3 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl enable flanneld.service [root@web01 ~]# systemctl restart flanneld.service 단계 4: flannel 네트워크 인터페이스 확인 1 [root@web01 ~]# ifconfig flannel.1 인터페이스가 표시되어야 합니다 (예시 출력):\n1 2 3 4 5 6 7 8 flannel.1: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1450 inet 172.16.5.0 netmask 255.255.255.255 broadcast 0.0.0.0 inet6 fe80::f06c:96ff:fefe:b7ab prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether f2:6c:96:fe:b7:ab txqueuelen 0 (Ethernet) RX packets 315 bytes 26460 (25.8 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 314 bytes 34636 (33.8 KiB) TX errors 0 dropped 29 overruns 0 carrier 0 collisions 0 단계 5: docker 구성 파일 수정 flannel이 생성한 서브넷 환경 변수를 확인합니다: 1 [root@web01 ~]# cat /run/flannel/subnet.env 출력 예시:\n1 2 3 4 FLANNEL_NETWORK=172.16.0.0/16 FLANNEL_SUBNET=172.16.5.1/24 FLANNEL_MTU=1450 FLANNEL_IPMASQ=true 1 [root@web01 ~]# cat /run/docker_opts.env 출력 예시:\n1 2 3 4 DOCKER_OPT_BIP=\u0026#34;--bip=172.16.5.1/24\u0026#34; DOCKER_OPT_IPMASQ=\u0026#34;--ip-masq=false\u0026#34; DOCKER_OPT_MTU=\u0026#34;--mtu=1450\u0026#34; DOCKER_OPTS=\u0026#34; --bip=172.16.5.1/24 --ip-masq=false --mtu=1450\u0026#34; docker 서비스 구성을 수정합니다: 1 [root@web01 ~]# vim /usr/lib/systemd/system/docker.service [Service] 섹션에 다음을 추가합니다:\n1 2 3 ExecStart=/usr/bin/dockerd $DOCKER_OPTS -H fd:// --containerd=/run/containerd/containerd.sock ExecReload=/bin/kill -s HUP $MAINPID EnvironmentFile=-/run/docker_opts.env docker 서비스를 재시작합니다: 1 2 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl restart docker.service 단계 6: docker 네트워크 확인 nginx 컨테이너를 생성하고 접속합니다: 1 2 3 4 5 6 7 8 9 [root@web01 ~]# ifconfig docker0: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1450 inet 172.16.5.1 netmask 255.255.255.0 broadcast 172.16.5.255 inet6 fe80::42:f6ff:fe5d:5423 prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether 02:42:f6:5d:54:23 txqueuelen 0 (Ethernet) RX packets 10073 bytes 407167 (397.6 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 48185 bytes 38083646 (36.3 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 1 2 [root@web01 ~]# docker run -d -p 8800:80 nginx [root@web01 ~]# docker exec -it \u0026lt;컨테이너 ID\u0026gt; /bin/bash 컨테이너 IP를 확인합니다 (예시: 172.16.5.3): 1 2 root@컨테이너 ID:/# ifconfig eth0: inet 172.16.5.3 netmask 255.255.255.0 4. web02 호스트 구성 (Host2: 192.168.0.208) 단계 설명 구성 단계는 web01과 완전히 동일하며, 다음 매개변수만 수정하면 됩니다: --iface=192.168.8.99 (로컬 IP) docker 컨테이너 IP 예시: 172.16.5.2 (flannel에 의해 자동 할당) 5. 실험 확인 web01 컨테이너에서 web02 컨테이너 IP (예시: 172.16.9.2)로 ping을 수행합니다:\nweb02에서 컨테이너를 생성하고 컨테이너 IP 주소를 확인합니다.\n결과: 컨테이너 간 통신이 성공하고, flannel 네트워크 구성이 완료되었습니다.\n부록: etcd 네트워크 구성 조회 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/config /coreos.com/network/config {\u0026#34;Network\u0026#34;: \u0026#34;172.16.0.0/16\u0026#34;, \u0026#34;SubnetLen\u0026#34;: 24, \u0026#34;SubnetMin\u0026#34;: \u0026#34;172.16.1.0\u0026#34;,\u0026#34;SubnetMax\u0026#34;: \u0026#34;172.16.10.0\u0026#34;, \u0026#34;Backend\u0026#34;: {\u0026#34;Type\u0026#34;: \u0026#34;vxlan\u0026#34;}} 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/subnets --prefix --keys-only /coreos.com/network/subnets/172.16.5.0-24 /coreos.com/network/subnets/172.16.9.0-24 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/subnets/172.16.5.0-24 /coreos.com/network/subnets/172.16.5.0-24 {\u0026#34;PublicIP\u0026#34;:\u0026#34;192.168.8.188\u0026#34;,\u0026#34;PublicIPv6\u0026#34;:null,\u0026#34;BackendType\u0026#34;:\u0026#34;vxlan\u0026#34;,\u0026#34;BackendData\u0026#34;:{\u0026#34;VNI\u0026#34;:1,\u0026#34;VtepMAC\u0026#34;:\u0026#34;5a:67:18:61:6a:5d\u0026#34;}} ","permalink":"https://LMDAWN1127.github.io/ko/posts/flannel-network-lab/","summary":"\u003ch1 id=\"flannel-실전-docker-크로스-호스트-컨테이너-네트워크-구축\"\u003eflannel 실전: Docker 크로스 호스트 컨테이너 네트워크 구축\u003c/h1\u003e\n\u003ch2 id=\"1-네트워크-환경-계획\"\u003e1. 네트워크 환경 계획\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003eetcd 호스트 IP: 192.168.8.88 호스트명: docker-network\u003c/li\u003e\n\u003cli\u003eHost1 IP: 192.168.8.188 호스트명: web01\u003c/li\u003e\n\u003cli\u003eHost2 IP: 192.168.8.99 호스트명: web02\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"2-etcd-호스트-구성\"\u003e2. etcd 호스트 구성\u003c/h2\u003e\n\u003ch3 id=\"단계-1-etcd-다운로드-및-압축-해제\"\u003e단계 1: etcd 다운로드 및 압축 해제\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003egithub.com에서 etcd-v3.6.0-linux-amd64.tar.gz 소프트웨어 패키지를 다운로드합니다\u003c/li\u003e\n\u003cli\u003e소프트웨어 패키지를 etcd 호스트의 /root 디렉토리에 복사하고 압축을 해제합니다:\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# tar -zxvf etcd-v3.6.0-linux-amd64.tar.gz\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"단계-2-etcd-실행-파일-설치\"\u003e단계 2: etcd 실행 파일 설치\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# cd etcd-v3.6.0-linux-amd64\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network etcd-v3.6.0-linux-amd64\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# cp etcd* /usr/local/bin/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"단계-3-데이터-디렉토리-생성\"\u003e단계 3: 데이터 디렉토리 생성\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# mkdir /var/lib/etcd\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"단계-4-etcd-서비스-구성-및-시작\"\u003e단계 4: etcd 서비스 구성 및 시작\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003eetcd.service 파일을 생성합니다:\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# vim /usr/lib/systemd/system/etcd.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e4\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e5\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e6\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eetcd_service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/local/bin/etcd --name etcd1 --data-dir /var/lib/etcd --listen-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379 --advertise-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col start=\"2\"\u003e\n\u003cli\u003e시스템 서비스 구성을 다시 로드하고 etcd를 시작합니다:\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# systemctl daemon-reload\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# systemctl enable etcd.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# systemctl restart etcd.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col start=\"3\"\u003e\n\u003cli\u003eetcd 리스닝 상태를 확인합니다:\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# netstat -tulnp |grep :2379\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etcp        \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e      \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e 192.168.8.88:2379       0.0.0.0:*               LISTEN      1912/etcd\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etcp        \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e      \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e 127.0.0.1:2379          0.0.0.0:*               LISTEN      1912/etcd\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"단계-5-etcd에-flannel-네트워크-구성-추가\"\u003e단계 5: etcd에 flannel 네트워크 구성 추가\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eetcdctl --endpoints http://127.0.0.1:2379 put /coreos.com/network/config \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{\u0026#34;Network\u0026#34;: \u0026#34;172.16.0.0/16\u0026#34;, \u0026#34;SubnetLen\u0026#34;: 24, \u0026#34;SubnetMin\u0026#34;: \u0026#34;172.16.1.0\u0026#34;,\u0026#34;SubnetMax\u0026#34;: \u0026#34;172.16.10.0\u0026#34;, \u0026#34;Backend\u0026#34;: {\u0026#34;Type\u0026#34;: \u0026#34;vxlan\u0026#34;}}\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eetcdctl --endpoints http://192.168.8.88:2379 get /coreos.com/network/config\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e구성 설명:\u003c/p\u003e","title":"flannel 실전: Docker 크로스 호스트 컨테이너 네트워크 구축"}]