[{"categories":["Container Tech","DevOps"],"content":"Harbor Active-Standby Deployment Environment Setup Operating System: CentOS 8.4 64bit\nHarbor Version: harbor-offline-installer-v2.11.1.tgz\nDocker Compose: docker-compose-linux-x86_64\nRole Hostname IP Address Primary Harbor Harbor01 192.168.8.161 Standby Harbor Harbor02 192.168.8.162 Docker Host web01 192.168.8.188 Harbor is an open-source enterprise Docker Registry project by VMware, built on the official Docker Registry with added features like management UI, role-based access control (RBAC), AD/LDAP integration, and audit logging. Each Harbor component runs as a Docker container, deployed using Docker Compose.\nPart 1: Deploy Primary Harbor (HTTP Mode, No Certificates) This method is suitable for internal networks where Harbor is accessed directly via HTTP without SSL certificates.\n1. Install Docker 1 2 3 4 5 6 7 8 9 10 11 12 13 # Remove existing yum repo files rm -rf /etc/yum.repos.d/* # Download repo file from Alibaba Cloud mirror curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo # Install 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 # Start Docker and enable on boot systemctl enable docker.service --now 2. Configure Docker Mirror 1 vim /etc/docker/daemon.json Enter the following content:\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. Install Docker Compose and Harbor 1 2 3 4 5 6 7 8 9 10 # Place docker-compose and harbor offline packages in /data ls /data # docker-compose-linux-x86_64 harbor-offline-installer-v2.11.1.tgz # Install docker-compose cp /data/docker-compose-linux-x86_64 /usr/local/bin/docker-compose chmod a+x /usr/local/bin/docker-compose # Extract harbor tar -zxvf /data/harbor-offline-installer-v2.11.1.tgz -C /usr/local/ 4. Modify Harbor Configuration 1 2 3 cd /usr/local/harbor/ cp harbor.yml.tmpl harbor.yml vim harbor.yml Modify the following key settings:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 hostname: 192.168.8.161 http: port: 80 # Comment out the entire https section (not needed for HTTP mode) #https: # port: 443 # certificate: /your/certificate/path # private_key: /your/private/key/path harbor_admin_password: Harbor12345 data_volume: /images Leave all other settings at their defaults.\n5. Run Installation 1 ./install.sh After installation, access http://192.168.8.161 in your browser and log in with admin / Harbor12345.\n6. Create Users and Projects Create user1 in the Harbor Web UI:\nLog in as user1:\nCreate a new project (e.g., openssh) as user1:\n7. Test Image Push and Pull On the Docker host (web01):\n1 2 3 4 5 6 7 8 9 10 11 # Log in to Harbor docker login 192.168.8.161 Username: user1 Password: # Login Succeeded # Tag the image docker tag nginx:latest 192.168.8.161/openssh/nginx:latest # Push to Harbor docker push 192.168.8.161/openssh/nginx:latest After successful push, the image appears in Harbor:\n1 2 3 # Remove local image and pull again docker image rm -f 192.168.8.161/openssh/nginx:latest docker pull 192.168.8.161/openssh/nginx:latest Harbor shows download count +1 after pull:\nPart 2: Deploy Standby Harbor The standby deployment is identical to the primary, just change the hostname to the standby IP.\n1 vim /usr/local/harbor/harbor.yml 1 hostname: 192.168.8.162 After deployment, access http://192.168.8.162 and create user2:\nLog in to standby Harbor as user2:\nPart 3: Configure Active-Standby Replication 1. Create Replication Endpoint on Primary Log in to primary Harbor as admin, go to Administration → Registries, and create a new endpoint:\nEnter standby information:\nEndpoint URL: http://192.168.8.162 Access ID / Access Secret: user2 credentials Creation successful:\n2. Create Replication Policy Go to Administration → Replications, create a new replication policy:\nDestination Registry: Select Harbor02 Mode: Event-Based (or other modes) 3. Verify Sync Results Push more images from Docker host to primary:\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 Check primary images:\nCheck standby images (auto-synced):\nThe standby shows synced images from primary, confirming active-standby replication is working.\nNote: Images that existed on primary before configuring replication need manual sync.\nPart 4: Enable HTTPS (Self-Signed Certificate) For production or public network access, HTTPS is recommended. Here\u0026rsquo;s the complete self-signed certificate workflow.\n1. Generate CA and Server Certificates On the Harbor server:\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 # Generate CA private key openssl genrsa -out ca.key 4096 # Generate CA certificate (valid for 10 years) openssl req -x509 -new -nodes -sha512 -days 3650 \\ -subj \u0026#34;/C=CN/CN=HarborRootCA\u0026#34; \\ -key ca.key \\ -out ca.crt # Generate server private key openssl genrsa -out harbor.key 4096 # Generate server certificate request (CN = Harbor IP or domain) openssl req -sha512 -new \\ -subj \u0026#34;/C=CN/CN=192.168.8.161\u0026#34; \\ -key harbor.key \\ -out harbor.csr 2. Configure Certificate Extensions Create v3.ext file with SAN (Subject Alternative Name):\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 must match Harbor\u0026rsquo;s actual access address, otherwise Docker client will report certificate domain mismatch errors.\n3. Sign Server Certificate 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. Enable HTTPS in Harbor Configuration 1 vim /usr/local/harbor/harbor.yml Uncomment the https section and update certificate paths:\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 must match IP.1 in v3.ext.\n5. Redeploy Harbor 1 2 3 4 cd /usr/local/harbor/ docker-compose down ./prepare docker-compose up -d Warning: Do NOT use docker-compose down -v, as -v deletes named volumes, causing loss of Harbor database and user data.\nAccess via https://192.168.8.161 after deployment.\n6. Configure Docker Clients to Trust Certificate Execute on every machine that needs to push/pull images:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Create certificate directory (name must be Harbor IP or domain) mkdir -p /etc/docker/certs.d/192.168.8.161 # Copy CA certificate to the directory # Method 1: scp from Harbor server scp root@192.168.8.161:/data/cert/ca.crt /etc/docker/certs.d/192.168.8.161/ # Method 2: Manually copy the ca.crt file content # Restart Docker systemctl restart docker # Test login docker login 192.168.8.161 # Enter admin and password, \u0026#34;Login Succeeded\u0026#34; indicates success Part 5: FAQ Q: Can I switch between HTTP and HTTPS modes?\nYes. Modify the https section in harbor.yml, then re-run ./install.sh. Switching from HTTP to HTTPS requires generating certificates first; switching back to HTTP requires commenting out the https section.\nQ: Docker client reports \u0026ldquo;x509: certificate relies on legacy Common Name field\u0026rdquo; when logging into HTTPS Harbor?\nThis means the certificate lacks SAN (Subject Alternative Name). Regenerate the certificate ensuring v3.ext contains IP.1=\u0026lt;Harbor IP\u0026gt; and sign with -extfile v3.ext.\nQ: How to troubleshoot replication failures?\nCheck network connectivity between primary and standby, verify the target registry credentials, and ensure the user has write permission to the target project.\n","date":"2026-08-04","description":"","objectID":"/en/posts/harbor-master-slave/","readingTime":6,"tags":["Harbor","Docker","Image Registry","Replication","HTTPS"],"title":"Harbor Active-Standby Deployment: From Installation to HTTPS and Replication","url":"/en/posts/harbor-master-slave/","wordCount":1106},{"categories":["OpenStack"],"content":"I. OpenStack Core Components and Their Roles 1. HORIZON — Unified Web Dashboard Service Core function: Provides a Web-based Dashboard graphical management console for cloud administrators and tenants to visually allocate and monitor cloud resources. 2. NOVA — Compute Resource Management Service Core function: Manages and configures virtualized compute resources in the cloud environment (nova-compute). Role division: Controller node: Runs scheduling services such as nova-api, nova-scheduler, and nova-conductor. When a user requests a new cloud instance, nova-api receives and parses the request, and nova-scheduler selects the best compute node for load scheduling based on policy. Compute node: The worker node that actually hosts running cloud instances (supports heterogeneous hypervisors such as KVM, VMware ESXi, and Xen). High availability and performance bottlenecks of the controller node: The controller node hosts all API gateways, databases, and message queues, creating HA and performance pressure; 3. GLANCE — Image Service Core function: Responsible for discovery, registration, retrieval, and storage management of OS images. Common disk image formats: Common KVM format: qcow2 (supports thin provisioning and copy-on-write, COW); Common VMware format: vmdk. Deployment recommendation: Usually co-located and integrated on the controller node for high availability. 4. SWIFT — Object Storage Service Core function: Provides a highly available, distributed, eventually-consistent object storage service (Object Storage), purpose-built for static unstructured data, cloud instance system images, and full data backups. 5. NEUTRON — Software-Defined Networking (SDN) Core function: Provides network-as-a-service in the cloud environment, managing virtual routers, switches, security groups, floating IPs, and cross-node traffic connectivity. Network technologies: Fully supports VLAN, VXLAN, Geneve, and distributed virtual routing (DVR); Hardware SDN mode: Production environments typically deploy 2 dedicated network nodes to build an active-active or active-standby cluster. 6. CINDER — Block Storage Service Core function: Provides persistent block storage (cloud disks) for cloud instances, with dynamic attach, detach, snapshot, and resize. Underlying backend integration: Integrates with traditional storage arrays: Huawei OceanStor, IBM, EMC, and other high-end hardware storage; Integrates with open-source distributed storage: Ceph RBD, GlusterFS, etc. Underlying mechanism: When a user requests a cloud disk, Cinder passes the instruction down to the backend storage system—essentially carving and mapping out a LUN within physical or distributed storage. Comparison: Object storage (e.g., Baidu Netdisk, Swift, S3) cannot be formatted and mounted directly as a block device for a cloud instance\u0026rsquo;s filesystem, whereas a Cinder block device can be formatted and mounted directly. 7. HEAT — Orchestration Service Core function: Based on declarative templates (HOT templates or AWS CloudFormation format), it automates one-click batch deployment of multiple VMs, database clusters, network topologies, and monitoring policies via graphical orchestration or scripts. 8. CEILOMETER — Telemetry \u0026amp; Monitoring Service Core function: Collects performance metrics, uptime, and usage of physical and virtual resources. Public cloud scenario: Records API call volume, network egress, and compute hours, integrating with the billing system; Private cloud scenario: Monitors tenant quota levels, identifies over-use or idle resources, and supports capacity planning. 9. KEYSTONE — Unified Identity \u0026amp; Authorization Service Core function: The security cornerstone of OpenStack, responsible for tenant (Project), user (User), role (Role), service catalog, and Token authentication across the platform. Enterprise identity federation: Can seamlessly integrate with the enterprise\u0026rsquo;s existing Windows Active Directory (AD) or Linux OpenLDAP to deliver enterprise-grade unified-account single sign-on (SSO); Workflow approval and privilege isolation (Huawei Cloud as an example): Supports multi-level cloud instance request and approval workflows (up to 5 approval levels); Enforces the \u0026ldquo;separation of three powers\u0026rdquo; security principle (security officer, system administrator, and auditor manage with separated privileges); Supports integration with the customer\u0026rsquo;s in-house OA/BPM approval-flow systems. Quick Reference Table of Core Components Component Code Name Core Service Function Horizon Dashboard Unified Web Graphical Management Console Nova Compute Compute Resource Lifecycle \u0026amp; Instance Scheduling Glance Image VM Image Storage \u0026amp; Version Registration Swift Object Large-Scale Unstructured Object Storage Service Neutron Network Software-Defined Networking (SDN), Routing \u0026amp; Security Group Management Cinder Block Persistent Block Storage (Cloud Disk) Management for Instances Heat Orchestration Infrastructure-as-Code (IaC) Multi-Resource Stack Orchestration Ceilometer Metering Resource Usage Statistics, Monitoring Metering \u0026amp; Quota Analysis Keystone Identity Unified Identity Authentication, Token \u0026amp; Service Catalog Management II. OpenStack Deployment Planning (Victoria Edition) 1. Deployment Method Comparison Open-source manual deployment: Compile and configure each component individually—tedious and complex, usually for deep learning of underlying mechanisms; Tool-based automated deployment: Ansible deployment (e.g., OpenStack-Ansible, Kolla-Ansible); Packstack deployment: Red Hat\u0026rsquo;s open-source, Puppet-based automated rapid-delivery tool (used in this walkthrough); 2. Cluster Node Specifications and Network Planning (1) Controller Node (1 unit) Hosted components: API services: nova-api, cinder-api, glance-api, neutron-server, etc.; Underlying base services: MariaDB / MySQL, RabbitMQ message queue, Memcached, Keystone; Network and management services: external routing on the network node, local YUM cache, and Chrony NTP server. System requirements: CPU: 4 vCPU minimum Memory: 4GB minimum Disk: 100GB available disk space NICs: 3 network cards (NIC 1 in bridged mode for Internet access; NIC 2 and NIC 3 in host-only mode for internal traffic) IP address planning: Internal management IP: 192.168.100.10 External access IP: 192.168.10.10 (2) Compute Node (1 unit) Hosted components: nova-compute, and the network L2 Agent (Open vSwitch Agent) System requirements: CPU: 4 vCPU minimum Memory: 8GB minimum (4GB at least) Disk: 100GB disk space NICs: 3 network cards (all in host-only mode) IP address planning: Compute: 192.168.100.11 (3) YUM Repository Server + Chrony NTP Server (1 unit) System requirements: CPU: 2 vCPU Memory: 2GB NICs: 2 cards (one bridged to the Internet for public time sync and upstream repos, the other host-only to serve internal nodes) IP address planning: External IP: 192.168.10.11 Internal IP: 192.168.100.20 Environment downgrade note: If your personal computer is short on hardware, you can compress down to a minimum of 2 VMs (the controller node also acts as the YUM source and NTP server, plus 1 compute node). Note: the system clocks of all nodes must stay highly consistent—clock drift must not exceed 5 minutes, otherwise Keystone handshakes and message-queue communication will fail entirely.\nIII. Full Automated Deployment Walkthrough Phase 1: YUM Repository Server and Chrony NTP Server Setup 1. Configure Network Interfaces 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. Configure the internal communication 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. Configure the Internet-facing NIC ens192 (for syncing public 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 # Restart the NICs to apply the configuration [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. Configure Local CD-ROM YUM Repository 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/ # Mount the OS installation ISO mount /dev/cdrom /media/ # Write the local CD-ROM repository config file [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. Configure Chrony NTP Server Because OpenStack is extremely strict about inter-node clock synchronization, configure the time source first:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # Edit /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 # Restart and verify the time sync service [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 # Disable the firewall and 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. Build an httpd-based Local Web YUM Repository Publish the CentOS 8 and OpenStack Victoria software repositories as an HTTP mirror source for all cluster nodes to pull from.\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. Install and start the Apache web service [root@Cloud ~]# yum -y install httpd [root@Cloud ~]# systemctl enable httpd --now # 2. Create directories and mount the image files [root@Cloud ~]# mkdir /isos [root@Cloud ~]# mkdir -p /var/www/html/centos8 [root@Cloud ~]# mkdir -p /var/www/html/openstack # Mount the base system CD and the OpenStack Victoria installation image [root@Cloud ~]# mount /dev/cdrom /var/www/html/centos8/ [root@Cloud ~]# mount /isos/26-CentOS8-4-OSP-Victoria.iso /var/www/html/openstack/ # 3. Persist the mounts in /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. Configure the HTTP YUM repository files [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. Distribute the prepared repo files to each controller and compute node [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/ Phase 2: System Environment Initialization for Each Cluster Node 1. Controller Node Initialization (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. Configure the management network 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. Configure the external network 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. Configure the Chrony NTP client [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. Set the hostname [root@Controller ~]# hostnamectl set-hostname Controller # 5. Disable the firewall and 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. Configure cluster /etc/hosts resolution [root@Controller ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 7. Clean the default system repo cache [root@Controller ~]# cd /etc/yum.repos.d [root@Controller yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ 2. Compute Node Initialization (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. Configure the management network [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. Configure the Chrony NTP client [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. Set the hostname [root@Compute ~]# hostnamectl set-hostname Compute # 4. Disable the firewall and 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. Configure cluster hosts resolution [root@Compute ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 6. Clean the default system repo [root@Compute ~]# cd /etc/yum.repos.d [root@Compute yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ ⚠️ Key checkpoint: It is recommended to shut down all VMs at this point and take a unified snapshot for quick rollback if something goes wrong.\nPhase 3: Packstack Automated Answer-File Deployment 1. Install the Packstack Deployment Tool Run on the controller node (Controller):\n1 [root@Controller ~]# yum -y install openstack-packstack 2. Generate the Default Answer File 1 [root@Controller ~]# packstack --gen-answer-file=/root/answers.txt Networking note: OpenStack Victoria supports both OVN (Open Virtual Network) and the traditional OVS (Open vSwitch).\n3. Customize the Answer File Parameters 1 2 # Replace the auto-detected external IP with the internal management 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 Edit /root/answers.txt and review and modify the following core configuration items:\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. Specify the compute node cluster IP list (comma-separated) CONFIG_COMPUTE_HOSTS=192.168.100.11 # 2. Set the Keystone admin root initial password CONFIG_KEYSTONE_ADMIN_PW=000000 # 3. Disable the Demo tenant network (production hardening) CONFIG_PROVISION_DEMO=n # 4. Enable Heat orchestration component support CONFIG_HEAT_INSTALL=y # 5. Neutron core driver and network type configuration (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 external network bridge binding configuration (associates the physical external NIC ens224) CONFIG_NEUTRON_OVS_BRIDGE_MAPPINGS=extnet:br-ex CONFIG_NEUTRON_OVS_BRIDGE_IFACES=br-ex:ens224 # 7. Preset storage quota capacity CONFIG_SWIFT_STORAGE_SIZE=20G CONFIG_CINDER_VOLUMES_SIZE=50G ⚠️ Key checkpoint: After modifying the answer file, it is recommended to shut down all VMs again and take a snapshot.\n4. Execute One-Click Automated Deployment After powering on, run the deployment command on the controller node:\n1 [root@Controller ~]# packstack --answer-file=/root/answers.txt The deployment script uses Puppet to automatically complete software-source configuration, database table creation, message-queue cluster setup, certificate generation, and coordinated integration testing of all components across all nodes. When finished, it prints the Dashboard login URL and the keystonerc_admin environment-variable file path to the terminal.\nIV. Linux System Log Management and OpenStack Troubleshooting 1. Linux Rsyslog Log Levels and Configuration Architecture In Linux systems, logs from the low-level system and most services are centrally managed by the rsyslog daemon.\nView the config file: 1 2 rpm -qc rsyslog # Core main config file: /etc/rsyslog.conf Log Rule Syntax Analysis 1 facility.level(priority) log storage path Common configuration examples:\n1 2 3 4 5 6 7 8 9 # 1. General system messages: # Log all services \u0026gt;=info level, but exclude mail, authpriv, and cron logs *.info;mail.none;authpriv.none;cron.none /var/log/messages # 2. Debug logs: *.debug /var/log/debug # 3. Error-level logs: *.err /var/log/error.log Syntax rule key points: mail: service facility; info: log priority level; .: denotes matching log messages at greater than or equal to (\u0026gt;=) this priority; *.info: means all services\u0026rsquo; \u0026gt;= info level logs are aggregated and written to the target file. Real-Time Dynamic Log Monitoring Commands 1 2 3 4 5 # Follow the tail output of the system log tail /var/log/messages # Continuously tail the live log stream tail -f /var/log/messages 2. OpenStack Custom Service Log Troubleshooting Guidelines Troubleshooting principle: Each component usually has its own custom log-file system (under /var/log/\u0026lt;component\u0026gt;/). If a component misbehaves, prioritize its dedicated logs; only fall back to the system-wide log /var/log/messages when no custom log exists.\nCase Study: Locating Neutron Network Service Failures During deployment or operation, if virtual network creation fails or an OVS port does not come up, you can directly capture errors and warnings via regex matching:\n1 2 3 4 5 # 1. Regex-filter errors and warnings across all Neutron logs tail -f /var/log/neutron/*.log | grep -iE \u0026#39;(err|warn)\u0026#39; # 2. Deep-troubleshoot the neutron-server core API service log, printing 3 lines of context before and after each match: tail -f /var/log/neutron/server.log | grep -iE -A3 -B3 \u0026#39;(err|warn)\u0026#39; Parameter description:\n-i: ignore case; -E: enable extended regex matching (err|warn); -A 3 (After): print 3 lines after the match; -B 3 (Before): print 3 lines before the match. ","date":"2026-10-06","description":"","objectID":"/en/posts/openstack-core-architecture-deployment/","readingTime":13,"tags":["OpenStack","Cloud Computing","Automated Deployment","Packstack","Victoria"],"title":"OpenStack Cloud OS: Core Architecture and Automated Deployment","url":"/en/posts/openstack-core-architecture-deployment/","wordCount":2716},{"categories":["OpenStack"],"content":"Architecture of Creating and Attaching Cinder Volumes Volume creation is handled by the Cinder node (the controller node), while attaching volumes is handled by Nova\nLocal LVM + iSCSI Volume Storage Configuration Specify in the answers.txt answer file that a Cinder volume should be created. Since the lab environment cannot provide dedicated storage hardware, the system uses local LVM as the Cinder backend The system creates a cinder-volumes volume group by default, 50 GB in size (specified manually by the user)\nThis volume group is created on the controller node\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 Call the LVM driver in /etc/cinder/cinder.conf so it recognizes the created volume group Treat this controller node as iSCSI storage (target) and share user-created volumes with the compute nodes over iSCSI When a user creates a 2 GB cloud disk, the system automatically invokes the LVM driver to create a 2 GB LV inside the volume group\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 When the user attaches this volume to the cloud server web01, Cinder sends a command to the storage side; the storage side then shares that LV with the compute host, so the compute host recognizes the disk, a new 2 GB disk appears, and libvirt invokes an XML file to add it to the cloud server\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; Storage-side configuration\nInstall the targetcli package and start the service 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 Target, service server, storage client 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 Connecting Cinder to NFS Storage Plan and configure the network according to the diagram above\n1. Configure NFS storage 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. Configure the Cinder node network (controller node) 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. Configure the compute node network 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. Configure 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 node\n1 2 [root@Controller ~]# cat /etc/hosts ####额外增加一条记录 192.168.8.20 storage Compute node\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-* In the OpenStack web UI, create a volume, choose nfs, and attach that volume to the cloud server\nIn the OpenStack web UI, create a volume, choose nfs, select an image as the source, and when creating the cloud server choose the newly created volume as the source\nOnce the cloud server is created, a volume is produced\nDelete the original cloud server, then create it again — this volume becomes usable\n","date":"2026-10-05","description":"","objectID":"/en/posts/openstack-cinder-block-storage/","readingTime":8,"tags":["OpenStack","Cinder","Block Storage","LVM","iSCSI","NFS"],"title":"OpenStack Cinder Block Storage Explained: Local LVM + iSCSI Volumes and NFS Backend Integration","url":"/en/posts/openstack-cinder-block-storage/","wordCount":1570},{"categories":["OpenStack"],"content":"Keystone Service OpenStack multi-domain support\n1 2 [root@Controller ~]# vim /etc/openstack-dashboard/local_settings OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True List all services in the system\n1 [root@Controller ~(keystone_admin)]# openstack service list Keystone address and API endpoint\n1 OS_AUTH_URL=http://192.168.100.10:5000/v3 All services know the Keystone address, because they need to ask Keystone to verify whether a user token is valid Keystone knows the addresses of all services, because every service registers itself with Keystone at creation time Endpoint addresses\n1 [root@Controller ~(keystone_admin)]# openstack endpoint list Each service has three endpoint addresses; these three URLs may be identical or different\npublic url — the URL used by external users\nadmin url — the URL used by administrators\ninternal url — the URL used for access between internal components\nCloud server provisioning flow\nThe user sends a request to Keystone with a username and password; once authentication succeeds, Keystone issues a token, and the user uses that token to send a create-cloud-server request to nova-api nova-api asks Keystone for help in validating whether that token is valid; if validation succeeds, the result is returned nova-api forwards the request to the nova-compute host The nova-compute host creates the cloud server according to the request and returns the result to nova-api nova-api returns the result to the user; the cloud server is created and its information is recorded in the database When I send a request to nova-api to create a server, I don\u0026rsquo;t necessarily know the nova-api address—but I do know the Keystone address. I query Keystone for the nova-api address, because Keystone stores the endpoint addresses of all services. Therefore I only need to know the Keystone address. The following shows how to look up the Keystone address and use it to resolve other service addresses.\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; When deploying OpenStack, a service is created\nEnter the database and create a database—for example, glance Create the database user glance and grant it ALL privileges (insert, delete, update, select) on all tables in the glance database Create the glance service and register the glance-api address (endpoint address) in the database When a user needs to access glance-api, they send a request to Keystone, which looks up the glance api endpoint address in its database\n1 2 #示例 如果将zlm用户加入admin角色，那他就拥有了admin角色权限 [root@Controller ~(keystone_admin)]# openstack role add --user zlm --project LMandMY_HOME admin When a user sends a request to nova-api, nova-api asks Keystone to verify the user\u0026rsquo;s identity and whether the token is still valid. It does not decide whether the user is authorized to create a cloud server—that depends on each service\u0026rsquo;s policy file\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 Service Controller node\nnova-api module\nnova-scheduler module\nnova-conductor module\nnova-novncproxy module\nCompute node\nnova-compute module\nnova-api\nHandles the externally exposed REST interface\nValidates and constrains the incoming parameters\nChecks and reserves quotas for the requested resources\nCreate, update, delete and query operations on resources\nThe entry point of the virtual machine lifecycle\nCan be deployed scaled out horizontally\nnova-conductor\nIt decouples nova-compute from direct database access: when a user requests a cloud server and creation succeeds, the nova-conductor module writes the server information into the database\ndatabase — stores information related to that component, such as cloud server info, image info and network info\nnova-scheduler\nWhen the OpenStack environment has multiple Compute nodes and a user creates a cloud server, the nova-scheduler module decides which compute node it runs on based on each compute host\u0026rsquo;s weight\nHAProxy provides load balancing: when a user wants to access nova-api, the request is handed to HAProxy, and depending on the load of each nova-api it decides which API node receives the request\nMQ — Message Queue (message queue)\nWhen a component consists of multiple modules, communication between modules goes through the message queue for congestion control and so on\nIf 1000 users send requests to nova-api\nnova-api sends the requests to nova-compute\nnova-api sends these 1000 requests to the message queue, which then delivers them to the nova-compute nodes in an orderly fashion according to each compute host\u0026rsquo;s message consumption and processing progress\nnova-compute\nThe module that actually runs virtual machines\nnova-novncproxy\nHow it works: the nova-compute host opens a dedicated VNC Server port for each running virtual machine\nThe nova-compute host must not expose that port to the outside; otherwise any host on the Internet could access the port and the virtual machine\nTo solve this, a nova-novncproxy service is started on the controller node and exposed externally. When a user opens that VM\u0026rsquo;s console, the request first goes to nova-novncproxy, which uses the supplied information (VM UUID, token, etc.) to find out which nova-compute runs the VM, and then forwards the request to that Compute host.\nHow opening a VM console via VNC works\nvncserver — port 5900\nvncviewer — vncviewer\nThe client does not need to install vncviewer; instead the functionality is integrated into browser HTML via the noVNC protocol\nGlance and Swift Service I. Deploying a cloud server:\nAfter the user issues a command to deploy a cloud server and it is validated, the nova-compute host downloads the image from the glance image server to local storage (the server itself also runs locally) — /var/lib/nova/instances/_base/\nAfter the cloud server boots, a differential disk is created; new writes go into that differential disk\nLab: store glance images in Swift object storage instead of on glance\u0026rsquo;s local disk\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 Note: the zlm user must be granted the swiftOperator role before it is allowed to operate on Swift\n","date":"2026-10-05","description":"","objectID":"/en/posts/openstack-keystone-glance-nova/","readingTime":8,"tags":["OpenStack","Keystone","Glance","Nova","Endpoint","Image Service"],"title":"OpenStack Core Services Explained: Keystone Authentication, Nova Compute, and Glance/Swift Image Storage","url":"/en/posts/openstack-keystone-glance-nova/","wordCount":1675},{"categories":["OpenStack"],"content":"I. Provisioning a Cloud Server via the Web Dashboard The admin user is the cloud super administrator\nadmin manages identities\nCreate a tenant (project) Create a user and associate it with a project Upload an image. Images uploaded by the admin can be set as public so all tenants can access them; images uploaded by ordinary users are private and can only be shared within the tenant. Create a flavor Create an external network Whichever node the L3 agent is deployed on becomes the network node\nLog in as a tenant\nLog in with the tenant identity Create a private network and subnet (VPC) Create a router and attach the external and private networks Create a security group and rules Create a key pair Provision the cloud server and associate a floating IP Testing (login, hostname, network) cloud-init initializes custom information such as hostname and password\nSuppose I create an image whose content is very large—about 20 GB of disk and requiring at least 4 GB of RAM to run smoothly. But the tenant doesn\u0026rsquo;t know this; when provisioning a server they pick a flavor with only 10 GB disk and 2 GB RAM, causing the provisioning to fail without understanding why. Therefore, when uploading the image I specify a minimum disk of 20 GB and minimum RAM of 4 GB, so that later, when choosing a flavor, any flavor smaller than that cannot be selected—only flavors meeting the requirement can be chosen.\nThe following is the key command in OpenStack Nova used to discover and map newly added compute nodes into their owning Cell database. Under the Cells v2 architecture, every compute node must complete this registration step; otherwise the scheduler cannot deploy instances on it and the API will not list that host.\n1 [root@Controller ~(keystone_admin)]# nova-manage cell_v2 discover_hosts --verbose A cloud server can reach the Internet through the router. The point of configuring a floating IP is to expose services to the outside—e.g., a web server can go out via SNAT, but remote users cannot come in to access that service. The following shows how to inspect the iptables rules OpenStack uses under the hood.\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. */ Lab requirement: create a cloud server that can access the Internet, point its yum repository to Alibaba Cloud, install the httpd package, start the service, and access the web page http://192.168.10.182 from an external host.\nA Compute host is essentially a KVM hypervisor. When OpenStack provisions a cloud server, the scheduler distributes it onto one of the Compute hosts.\nII. Provisioning a Cloud Server via the Command Line 1 [root@Controller ~(keystone_admin)]# cp keystonerc_admin keystonerc_zlm Requirement 1: Delete all resources using the 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 Delete the cloud server 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 Delete the volume 1 2 [root@Controller ~(keystone_zlm)]# openstack volume list [root@Controller ~(keystone_zlm)]# openstack volume delete data01 Delete the router 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 Delete the security group 1 2 [root@Controller ~(keystone_zlm)]# openstack security group list [root@Controller ~(keystone_zlm)]# openstack security group delete SG01 Delete the network 1 2 [root@Controller ~(keystone_zlm)]# openstack network list [root@Controller ~(keystone_zlm)]# openstack network delete VPC01 Switch to the admin identity to delete the public network, images, flavors, tenants, users, etc.\n1 [root@Controller ~(keystone_zlm)]# source keystonerc_admin Delete the public network 1 2 [root@Controller ~(keystone_admin)]# openstack network list [root@Controller ~(keystone_admin)]# openstack network delete public Delete the image 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 Delete the flavor 1 2 [root@Controller ~(keystone_admin)]# openstack flavor list [root@Controller ~(keystone_admin)]# openstack flavor delete m3.web Delete the tenant and user 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 Requirement 2: Create all resources via the CLI—provision a server, add a floating IP, access the server, and create networks\n1 [root@Controller ~(keystone_admin)]# source keystonerc_admin Create a project and user, and associate the user with the project 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 Upload an image 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 Create a flavor 1 [root@Controller ~(keystone_admin)]# openstack flavor create --ram 1024 --disk 10 --public m3.web Create the public network and subnet 1 [root@Controller ~(keystone_admin)]# openstack network create --external --share --provider-network-type flat --provider-physical-network extnet --project LMandMY_HOME public Create the subnet\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 Switch to the tenant identity\n1 [root@Controller ~(keystone_admin)]# source keystonerc_zlm Create the network and subnet 1 [root@Controller ~(keystone_zlm)]# openstack network create VPC01 Create the subnet\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 Create the router 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 Create the security group 1 [root@Controller ~(keystone_zlm)]# openstack security group create SG01 Allow the ICMP protocol\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol icmp --remote-ip 0.0.0.0/0 SG01 Allow the SSH service\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol tcp --dst-port 22 --remote-ip 0.0.0.0/0 SG01 Allow port 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 Create a floating IP 1 [root@Controller ~(keystone_zlm)]# openstack floating ip create public Create a key pair 1 [root@Controller ~(keystone_zlm)]# openstack key create key01 \u0026gt; key01.pem Create the cloud server 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 Associate the floating IP with the cloud server\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 Access the cloud server\n1 2 [root@Controller ~(keystone_zlm)]# chmod 400 key01.pem [root@Controller ~(keystone_zlm)]# ssh -i key01.pem cirros@192.168.10.182 If the external NIC cannot communicate\n1 [root@Controller ~(keystone_zlm)]# ifup br-ex ; ifup ens224 ","date":"2026-10-04","description":"","objectID":"/en/posts/openstack-provision-cloud-server/","readingTime":8,"tags":["OpenStack","Cloud Server","Horizon","Neutron","Nova","CLI"],"title":"OpenStack Cloud Server Provisioning","url":"/en/posts/openstack-provision-cloud-server/","wordCount":1616},{"categories":["MySQL"],"content":"01 - Organize the systemctl database Service files Option 1: Standard production daemon configuration 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 Option 2: With config file and file-handle limits 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 After the modification\n1 2 3 [root@master local]#systemctl daemon-reload [root@master local]#systemctl restart mysqld.service [root@master local]#systemctl enable mysqld.service 02 - Multi-version MySQL (5.6 / 5.7) install \u0026amp; upgrade-prep practice Step 1: Download the software archives 1 2 3 4 5 # MySQL 5.7.40 binary package [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 binary package [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.48-linux-glibc2.12-x86_64.tar.gz Step 2: Extract, install, and create symlinks 1 2 3 4 5 6 7 8 9 # install version 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 # Note: during multi-version coexistence debugging, do not set the global env var yet to avoid command-path conflicts # install version 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 # Note: do not configure the global env var for now Step 3: Initialize the database software MySQL 5.7 initialization (data directory 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 initialization (data directory 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 # missing Perl module [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 # check whether the output contains two OKs and confirm the exit code: [root@master local]#echo $? 0 Step 4: Write the multi-instance dedicated config files MySQL 5.7 (instance 3357) configuration:\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 (instance 3356) configuration:\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 Step 5: Run, start, and verify management Start/Stop the MySQL 5.7 service:\n1 2 3 4 5 6 7 8 9 10 11 # Method 1: start [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 # Method 2: start [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. Start/Stop the MySQL 5.6 service:\n1 2 3 4 5 6 7 8 9 10 11 # Method 1: start [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 # Method 2: start [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. Verify the status of the three services (all 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":"/en/posts/systemd-mysql-multiversion/","readingTime":3,"tags":["MySQL","systemd","Multi-version Deploy"],"title":"systemd Service Management \u0026 Multi-version MySQL Deployment","url":"/en/posts/systemd-mysql-multiversion/","wordCount":631},{"categories":["MySQL"],"content":"01 - Database Service Installation and Deployment 1.1 Download the database server package Official download address (historical archived binary packages):\n1 2 3 # storage directory [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 Check the package information\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 Install the database server from a binary package Step 1: Prepare the OS environment 1. Confirm the network address 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. Configure hostname resolution 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. Disable the system firewall 1 [root@master local]#systemctl disable firewalld --now 4. Disable SELinux 1 2 3 4 5 [root@master local]#sed -i \u0026#39;s/^SELINUX=enforcing/SELINUX=disabled/\u0026#39; /etc/selinux/config # reboot to take effect [root@master local]#reboot [root@master local]#getenforce Disabled 5. Avoid conflicts during database installation 1 2 3 4 5 6 # check whether a bundled mariadb is present on the system [root@master local]#rpm -qa | grep mariadb mariadb-libs-5.5.68-1.el7.x86_64 # uninstall the bundled database packages [root@master local]#yum remove -y mariadb-libs 6. Install DB dependency packages (missing ones cause init errors) 1 [root@master local]#yum install -y libaio-devel Step 2: Install the database software 1. Extract the package and create a symlink (for easier version management) 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. Configure the system-wide environment variable 1 2 3 4 5 [root@master local]#vim /etc/profile # append at the end of the file export PATH=$PATH:/usr/local/mysql/bin # apply the configuration [root@master local]#source /etc/profile 3. Verify the installed version 1 2 [root@master local]#mysql -V mysql Ver 8.0.26 for Linux on x86_64 (MySQL Community Server - GPL) Step 3: Initialize the software MySQL has two core directories:\nProgram directory: /usr/local/mysql Data directory: /data/3306/data (stores the actual data) 1. Create a dedicated runtime user (no system login needed) 1 [root@master local]#useradd mysql -M -s /sbin/nologin 2. Create the data directory and set ownership 1 2 [root@master local]#mkdir /data/3306/data -p [root@master local]#chown mysql.mysql /data/3306/data 3. Initialize the data directory 1 2 3 4 5 # remove any interfering config files that may exist [root@master local]#rm -f /etc/my.cnf # run passwordless initialization (admin has no password afterward) [root@master local]#mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql Step 4: Write the database service config file Create and edit /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 Note: the basedir and datadir paths in the config must be adjusted to your actual environment.\nStep 5: Start the database service 1. Start with the official service management script 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! # being able to enter the database normally indicates a successful start [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. Start directly from the command line Start with the mysqld command: 1 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/data/3306/data --user=mysql --socket=/tmp/mysql.sock \u0026amp; Start with the mysqld_safe safe daemon: 1 /usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data \u0026amp; 3. Start with systemctl / service Method 1: write a dedicated Systemd unit file (/etc/systemd/system/mysqld.service) Method 2: directly enable and invoke the service script under /etc/init.d/: 1 2 3 4 systemctl enable mysqld service mysqld start # or systemctl start mysqld 1.3 Supplementary knowledge 1. Comparing the two initialization methods Method 1: insecure initialization (--initialize-insecure)\n1 mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql Characteristic: the DB admin has no password and can log in directly without one, convenient for testing or scripted initialization. Method 2: secure initialization (--initialize)\n1 mysqld --initialize --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql Characteristic: the DB admin gets a random temporary password printed in the log, used for the first login. Caution: the temporary password is only for the first login; after logging in you must reset a new password, and no other operations can run before changing it: 1 ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; 2. Standard troubleshooting flow for startup failures Step 1: Confirm the basic system environment Check whether SELinux is fully disabled; Check whether port 3306 is occupied and whether other conflicting DB service processes exist. Step 2: Investigate and analyze the error log Typical error: Starting MySQL. ERROR! The server quit without updating PID file (/data/3306/data/master.pid). Inspect the error log details: 1 2 3 cat /data/3306/data/master.err # example log error: # 2024-06-14T09:15:36.120048Z 1 [ERROR] [MY-012271] [InnoDB] The innodb_system data file \u0026#39;ibdata1\u0026#39; must be writable Step 3: How to troubleshoot when there is no error log Check the config file /etc/my.cnf: Confirm whether the config file path and filename are correct; Confirm whether the internal parameter syntax and path configuration are correct. 02 - Starting and stopping the database service There are several ways to start and stop the database service; the common ones are summarized below:\nNo. Start method Corresponding stop method Notes \u0026amp; suggestions 01 mysqld \u0026amp; kill / pkill / killall Not recommended; never use kill -9 02 mysqld_safe \u0026amp; kill / pkill / killall Direct kill not recommended 03 mysql.server start mysql.server stop Official native control script 04 service mysqld start service mysqld stop Recommended (classic service management) 05 systemctl start mysqld systemctl stop mysqld Recommended (modern Linux standard) 06 Client/admin command mysqladmin -uroot -p\u0026lt;password\u0026gt; shutdown Graceful shutdown via admin remotely or locally 07 Shut down within an SQL session Enter the terminal and run shutdown; Requires admin privileges 03 - Database service password management 1. Set a password when none exists Method 1: using the command line\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. Method 2: using an SQL statement\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. Change the password when one already exists Method 1: using the command line 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. Method 2: using an SQL statement 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. Forgot the password (password reset) Practical password reset procedure Step 1: Stop the database service\n1 mysql\u0026gt; shutdown; Or run in the system terminal:\n1 2 3 4 [root@master local]#service mysqld stop Shutting down MySQL.. SUCCESS! # or pkill mysqld Step 2: Start the database in safe mode (skip the grant tables to enable passwordless login)\n1 2 [root@master local]#/usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data --skip-grant-tables --skip-networking \u0026amp; [1] 2658 Note: --skip-networking disables network listening to prevent other network clients from intervening during the passwordless period.\nStep 3: Log in without a password and reset it\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; Run the following in the MySQL terminal in order:\n1 2 3 4 5 6 7 -- 1. Flush the privilege tables (sync in-memory and on-disk grant tables) -- Effect 1: write the in-memory grant table info to disk -- Effect 2: reload the on-disk grant table info into memory FLUSH PRIVILEGES; -- 2. Reset the password ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Note: if you run ALTER USER directly without FLUSH PRIVILEGES;, the system throws an error:\n1 ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement Step 4: Restart the database service normally\n1 2 3 4 5 6 7 # terminate the temporary safe-mode process [root@master local]#pkill mysqld # start the database service normally [root@master local]#/etc/init.d/mysqld start Starting MySQL.. SUCCESS! # or service mysqld start Step 5: Verify login with the new password\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":"/en/posts/mysql-binary-deploy/","readingTime":9,"tags":["MySQL","Database","Binary Install"],"title":"MySQL in Practice: Binary Installation, Start/Stop, and Password Management","url":"/en/posts/mysql-binary-deploy/","wordCount":1721},{"categories":["DevOps"],"content":"01. SonarQube Introduction 1 2 3 1. SonarQube is developed in Java and requires Open JDK 8 2. SonarQube depends on a MySQL database, at least version 5.6 3. A small SonarQube instance needs at least 4G of memory; a large instance needs 16G 02. Deployment 1. Install the JDK runtime 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. Install the database 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. Download the official MySQL Yum repository config file [root@sonarqube ~]#wget dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm # 2. Install the MySQL repository [root@sonarqube ~]#rpm -ivh mysql-community-release-el6-5.noarch.rpm Preparing... ################################# [100%] Updating / installing... 1:mysql-community-release-el6-5 ################################# [100%] # 3. Install the MySQL server [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. Start the MySQL service [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. Set the password [root@sonarqube ~]# mysqladmin -uroot password 000000 # 6. Create the sonarqube database [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. Install 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. Upload the SonarQube package [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. Extract into the current directory [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. Install plugins: Remove the default plugins: [root@sonarqube ~]# rm -rf /usr/local/sonarqube/extensions/plugins/* Upload the 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. Create a normal user [root@sonarqube ~]# useradd sonarqube # 5. Change ownership of the code to user/group 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. Configure the database connection [root@sonarqube ~]#vim /usr/local/sonarqube/conf/sonar.properties ... sonar.jdbc.username=root # database username sonar.jdbc.password=000000 # database password #----- 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 # enable local connection ... 4. Run SonarQube 1 2 SonarQube cannot be started as root; it must run as a normal user [root@sonarqube ~]# su - sonarqube -c \u0026#34;/usr/local/sonarqube/bin/linux-x86-64/sonar.sh start\u0026#34; 5. Configure the remote access token After startup the port is 9000; open http://11.0.1.202:9000/about in a browser, log in as admin / admin, and generate a token — it is shown only once, so record it.\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. Client deployment Client 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. Upload the client [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. Extract and create a symlink [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. Set the global PATH variable [root@jenkins ~]# export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 4. Persist the variable in /etc/profile [root@jenkins ~]# vim /etc/profile .. export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 5. Test the client by scanning code to the server for analysis Push the blog code under /var/lib/jenkins/workspace/test_job/ to SonarQube for testing [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 Execution succeeded:\nCheck the server: upload succeeded\n","date":"2026-10-02","description":"","objectID":"/en/posts/sonarqube-practice/","readingTime":5,"tags":["DevOps","SonarQube","CI/CD"],"title":"SonarQube in Practice: Self-hosted Deployment, Token Setup, and Code Quality Scanning","url":"/en/posts/sonarqube-practice/","wordCount":992},{"categories":["DevOps"],"content":"01. Jenkins Introduction Official site: jenkins.io\nJenkins is an open-source project — a Java-based continuous integration tool that monitors repetitive tasks and provides an open, easy-to-use platform to make software CI possible.\n02. Jenkins Deployment 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. Environment preparation 1. OS: CentOS 7.9 2. Memory: 2G for lab, at least 6G for production 3. Disable the firewall and turn off SELinux 4、JDK17 II. Install the JDK Upload the RPM package [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%] Check whether the installation succeeded: [root@jenkins ~]# rpm -qa|grep jdk jdk-17-17.0.11-7.x86_64 II. Install Jenkins Upload the Jenkins package [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. Change the Jenkins run user to root 1) Change the run user in the config file [root@jenkins ~]# vim /etc/sysconfig/jenkins ... JENKINS_USER=\u0026#34;root\u0026#34; ... 2) Change the run user in the service config [root@jenkins ~]# vim /usr/lib/systemd/system/jenkins.service ... User=root Group=root ... [root@jenkins ~]# systemctl daemon-reload 3) Install font configuration [root@jenkins ~]# yum -y install fontconfig IV. Start Jenkins and enable it on boot [root@jenkins ~]# systemctl start jenkins [root@jenkins ~]# systemctl enable jenkins 03. Log in to Jenkins 04. Change the password After changing the password you are logged out; log back in as admin\n05. Plugin installation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1. Upload plugins to the Jenkins home directory [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. Extract the plugins into the plugins directory [root@jenkins jenkins]#tar xf /root/jenkins_plu.tar.gz -C plugins/ 3. Restart Jenkins to apply [root@jenkins ~]# systemctl restart jenkins 06. Create a project Create a freestyle project and run pwd to see the default working directory Jenkins uses\nNote: after Jenkins creates a project, it automatically creates a workspace directory under its home /var/lib/jenkins/, and inside it a directory named after the project.\nSubsequently, the code Jenkins pulls from GitLab is stored under this project directory.\n1 2 [root@jenkins ~]#ls /var/lib/jenkins/workspace/ test_job Connect GitLab with Jenkins\nBefore it is connected, a \u0026ldquo;cannot connect\u0026rdquo; error is shown:\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 # configure keyless (SSH) access [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 # install git on Jenkins [root@jenkins ~]#yum install -y git Add the key generated by Jenkins into GitLab:\nRun git clone once on Jenkins to complete the initial yes prompt\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. Then the connection works\nTrigger another build to test the pull\nPull succeeded\nConfigure the webhook\nConfigure a webhook so Jenkins automatically pulls GitLab code and builds on each push\nSave both sides, then test\nAfter modifying the project, push again to test\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. Automatic build succeeded\n","date":"2026-10-01","description":"","objectID":"/en/posts/jenkins-practice/","readingTime":5,"tags":["DevOps","Jenkins","CI/CD"],"title":"Jenkins in Practice: Self-hosted Deployment, Plugin Setup, and GitLab-Driven CI","url":"/en/posts/jenkins-practice/","wordCount":956},{"categories":["DevOps"],"content":"01. GitLab Websites Official site: https://about.gitlab.com/\nChina mirror: https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/\n02. GitLab Deployment 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. Environment preparation 1. OS: CentOS 7.9 2. Memory: 4G for lab, at least 8G for production 3. Disable the firewall and turn off SELinux II. Download the GitLab RPM package [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. Install GitLab # During installation, dependencies are required [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 # install dependencies [root@gitlab ~]# yum -y install policycoreutils-python # continue installing GitLab after dependencies are ready [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. Configure the access domain or IP before starting [root@gitlab ~]# vim /etc/gitlab/gitlab.rb ... external_url \u0026#39;http://11.0.1.200\u0026#39; ... After the configuration, run the command: [root@gitlab ~]# gitlab-ctl reconfigure 03. GitLab Components 1 2 3 4 5 6 7 nginx: static web server gitlab-workhorse: lightweight reverse proxy logrotate: log file management tool postgresql: database redis: cache database sidekiq: runs queued background jobs (asynchronously). (Ruby) unicorn: An HTTP server for Rack applications; the GitLab Rails app is hosted on it. (Ruby Web Server, written mainly in Ruby) 04. Common GitLab Commands 1 2 3 4 5 gitlab-ctl start # start GitLab (services start by default after gitlab-ctl reconfigure) gitlab-ctl status # check the running status of all GitLab services gitlab-ctl stop # stop GitLab services gitlab-ctl stop nginx # stop a single service gitlab-ctl tail # view logs of all services 05. GitLab Web UI Configuration Log in to GitLab at: 11.0.1.200\n1. Login password After gitlab-ctl reconfigure finishes, it shows where the password is stored:\n2. Switch the UI language to Chinese 3. Change the default password 4. Allow local network requests for webhooks 06. GitLab Hands-on 1. Create a group 2. Create a project under the group At this point the first code repository is created, but it is empty. Next, upload code.\n3. Commit code into the project After the project is created, upload the blog code into this project.\nThere are two ways to connect the server with the code repository:\nFirst: connect using username and password.\nSecond: connect via SSH keyless authentication.\nWe use the keyless (SSH) method: convenient and more secure.\nGenerate a key pair (here we use the GitLab server directly). 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]-----+ View the key pair: [root@gitlab ~]# ll .ssh/ total 8 -rw------- 1 root root 1679 May 14 15:02 id_rsa # private key -rw-r--r-- 1 root root 393 May 14 15:02 id_rsa.pub # public key [root@gitlab ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJGpCKL64eidZ3I/nAVNCNJwD8yeDqelZP0GUTwlcp6IdKaZ7wUOIRDCtGn5r+YnOCKNwMhUNzb3VCOSVLDVIE0x53dPrCMK4OKLmZ6OtTYFBveGOZWWwdYyGQITx2eP7zOSBxeW3AnEWcya/UZdiZrYXA/d29DWjMjCt5QAxOit0EWSir1Z4i2MkTPuljOmjk6A55XAoem0WJRdg/d8KKIiCdcGrj4adYq2kNJ8oYH/9QcnpASUW/ANhzrALnLIX2WaH/p0T3gisLvk2KDUe3rVKD3XaKDlyLgWIViQFm3Me6L2e4taRFiu3VdsbfFfzjDevfDtLaqKLOqPtaczlF root@gitlab Paste the generated public key into the SSH key management page of the GitLab UI. This completes the SSH keyless setup between the server and the GitLab UI. Next, push the code into the blog project.\nThere are two ways to push code:\nClone the empty repo locally, copy the code into it, then commit to the remote GitLab repo. Go into the project and find the project 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. Copy the link and run the command. [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. Verify the blog repo was downloaded locally. [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. Enter the blog directory. [root@gitlab ~]#cd blog/ 4. Extract the code: [root@gitlab blog]#tar xf /root/blog_backup_20261001-1435.tar.gz -C . 5. Commit the code to the local repository. [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. Push the code to the blog repo on the remote GitLab. Since we cloned the GitLab repo directly, the local directory already has the remote blog project address. Check the remote repository: [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) Push the local master branch to the remote 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. View the blog project; the code upload succeeded.\nPush a local Git repo to the remote 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. Set up a local repository. [root@gitlab ~]#mkdir dawn [root@gitlab ~]#cd dawn 2. Initialize the repository. [root@gitlab dawn]#git init 3. Upload the code. [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. Set the GitLab blog repo as the local remote. [root@gitlab dawn]#git remote add origin git@11.0.1.200:dawn/blog.git 5. Push local content to the remote (the remote already has content, so a direct push errors; keep the remote content by pulling/merging first, then pushing). [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. Create a normal user \u0026ldquo;dev\u0026rdquo;. After creation, change the dev password — do not log out; click Edit on the user page.\nAfter that, log in to GitLab as dev (you must change the password on first login).\nAdd the dev user to the dawn group. Generate a key pair on the dev server. 1 [root@lb01 ~]#ssh-keygen Copy the generated public key into the SSH-key page under the dev account. Clone the code to the dev host 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. Modify and upload code to the remote repository. 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 Set the user identity on 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; Commit to the remote repository. [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; # pushing to the master branch is not allowed Push to another branch: 1. Create the dev branch. [root@lb01 blog]#git branch dev [root@lb01 blog]#git branch dev * master 2. Push the dev branch to the remote. [root@lb01 blog]#git push -u origin dev Merge Request (MR)\nInitiate a merge request from the dev account. In the root account, approve the merge and delete the source dev branch. Download the code on the web server. 1 2 [root@web01 ~]#git clone http://11.0.1.200/dawn/blog.git Enter the username and password. ","date":"2026-10-01","description":"","objectID":"/en/posts/gitlab-practice/","readingTime":8,"tags":["DevOps","GitLab","CI/CD"],"title":"GitLab in Practice: Self-hosted Deployment, SSH Keyless Access, and Repository Management","url":"/en/posts/gitlab-practice/","wordCount":1675},{"categories":["DevOps"],"content":"01. Install Git and Initialize a Repository 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 Commands 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. Check repository status (always keep the working tree clean) [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. Create a plain file [root@gitlab dawn]#touch a.txt 3. Stage the working tree content [root@gitlab dawn]#git add a.txt # git add . stages everything in the working tree [root@gitlab dawn]#git add . 4. Remove content from the staging area [root@gitlab dawn]#git rm --cached a.txt rm \u0026#39;a.txt\u0026#39; 5. Stage it and commit to the local repository [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 # # commit to the local repository with git commit -m plus a message [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 Check status: [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean Create a file touch a.txt Stage git add a.txt Commit to local repo git commit -m \u0026#34;xxx\u0026#34; 2. Deleting with 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 Note: if you regret staging a file, remove it from the staging area [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. Restore the working tree content [root@gitlab dawn]#git reset HEAD b.txt [root@gitlab dawn]#git checkout -- b.txt 4. Diffing with git git diff compares the working tree against the staging area [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 compares the staging area against the local repository [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. Renaming a file [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. Roll back to a specific snapshot Show all snapshots: [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 Roll back with 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. View all historical actions: [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 branches 1. List branches (* marks the current branch) [root@gitlab dawn]#git branch * master 2. Create a branch [root@gitlab dawn]#git branch dev [root@gitlab dawn]#git branch dev * master 3. Switch to the dev branch [root@gitlab dawn]#git checkout dev Switched to branch \u0026#39;dev\u0026#39; [root@gitlab dawn]#git branch * dev master 4. Create a file on the branch and commit it to the local repository [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. Merging branches First switch back to the master branch [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master Merge the dev branch into 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. Delete the branch right after finishing the feature (it falls behind the remote branch) [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 2694c7f). [root@gitlab dawn]#git branch * master 7. Merge conflicts ---------------------------------- Editing a file does not necessarily block the merge Edit the file on the master branch and add bbbbb [root@gitlab dawn]#vim b.txt aaaaa bbbbb Create and switch to the dev branch with a single command [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 Edit the content of b.txt [root@gitlab dawn]#vim b.txt aaaaa bbccc Commit to the local repository [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;modifle bbbccc\u0026#34; Switch back to master and merge the dev branch [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 ------------------------------------------------------ A case that triggers a merge conflict Create the dev branch [root@gitlab dawn]#git branch dev On the master branch, edit line 3 of b.txt [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(+) Switch to the dev branch and edit line 3 of b.txt [root@gitlab dawn]#cat b.txt aaaaa iiiii ccccc Commit to the local repository [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(+) Switch back to master and merge the branch [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. Inspect the file to see the conflict markers [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 Resolve the conflict manually, keeping one side or both [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 Delete the dev branch after merging [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 925c06c). ","date":"2026-10-01","description":"","objectID":"/en/posts/git-practice-notes/","readingTime":8,"tags":["DevOps","Git","Version Control"],"title":"Git in Practice: Installation, Essential Commands, and Branch Management","url":"/en/posts/git-practice-notes/","wordCount":1566},{"categories":["Zabbix","Monitoring \u0026 Ops"],"content":"1. Get the Number of Logged-in Users 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. Define It as a Zabbix Monitoring Item 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 can be used to check the item\u0026#39;s syntax [root@web02 ~]#zabbix_agentd -p|grep login_number login_number [t|2] # restart the agent [root@web02 ~]# systemctl restart zabbix-agent.service 3. Collect and Monitor from the Zabbix Server Side From the server, use the command line to test whether the custom value can be fetched through the monitoring item.\n1 2 3 4 # install the zabbix-get command [root@zabbix ~]# yum -y install zabbix-get [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k login_number 2 Add the monitoring item on the web page\nView the monitoring item\n4. Monitor Whether TCP Port 22 Is Alive Select the key value that matches the information you want to monitor.\nCheck whether the TCP port is in the LISTEN state. Returns 0 - not listening; 1 - listening.\nStop the sshd service on web02\n1 [root@web02 ~]# systemctl stop sshd 5. Configure a Custom Value Mapping for Display Apply the value mapping\n6. Custom Monitoring of Server Memory Percentage Step 1: Define a custom item to get the free-memory percentage\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] Step 2: Test fetching the key value from the server and add the monitoring item\n1 2 [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k mem_free 79.9775 7. Set a Trigger for Available Memory Trigger an alert when the available-memory percentage drops below 20%.\nTest: turn off the swap partition\n1 [root@web02 ~]# swapoff -a Use dd to stress-test and consume memory\n1 [root@web02 ~]# dd if=/dev/zero of=/dev/null bs=1200M count=2048 8. Configure a Multi-condition Trigger Trigger an alert only when both conditions are met simultaneously.\nAdd the swap-free percentage\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; Modify the trigger configuration\nThen run the dd stress test again to check the result.\n","date":"2026-09-23","description":"","objectID":"/en/posts/zabbix-custom-monitor/","readingTime":2,"tags":["Zabbix","custom-monitoring","UserParameter","trigger","value-mapping"],"title":"Custom Monitoring in Zabbix","url":"/en/posts/zabbix-custom-monitor/","wordCount":392},{"categories":["Zabbix","Monitoring \u0026 Ops"],"content":"For high-concurrency scenarios in enterprise Zabbix deployments.\n1. Prepare a Standalone Database Server (11.0.1.52) and Install 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. Export the Zabbix Database from the Zabbix Server and Import It into the New Database 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. Configure the Web UI\u0026rsquo;s Database Connection 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. Restart the Services 1 [root@zabbix ~]# systemctl restart zabbix-server httpd ","date":"2026-09-23","description":"","objectID":"/en/posts/zabbix-split-database/","readingTime":2,"tags":["Zabbix","database","split","high-concurrency"],"title":"Splitting the Zabbix Database","url":"/en/posts/zabbix-split-database/","wordCount":256},{"categories":["Zabbix","Monitoring \u0026 Ops"],"content":"1. Install the zabbix-agent Client — Prepare a Server (11.0.1.8) 1 2 3 4 5 6 7 8 Configure the repository: [root@web02 ~]# rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm Sync the yum repositories from the server to the client: [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 If the installation reports a package conflict: [root@web02 ~]# yum -y install centos-release-scl 2. Configure the zabbix-agent with the Server Info and Start It 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. Monitor web02 from Zabbix 4. web02 Monitoring Setup Complete ","date":"2026-09-23","description":"","objectID":"/en/posts/zabbix-monitor-server/","readingTime":1,"tags":["Zabbix","monitoring","zabbix-agent","monitoring-config"],"title":"Monitoring Server Information with Zabbix","url":"/en/posts/zabbix-monitor-server/","wordCount":107},{"categories":["Zabbix","Monitoring \u0026 Ops"],"content":"1. Configure the Repository 1 rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 2. Install zabbix-server and zabbix-agent 1 yum -y install zabbix-server-mysql zabbix-agent 3. Install the SCL Repository (to Resolve Version Conflicts) 1 [root@zabbix ~]# yum -y install centos-release-scl 4. Enable the Frontend Repository 1 2 3 4 5 [root@zabbix ~]# vim /etc/yum.repos.d/zabbix.repo [zabbix-frontend] ... enabled=1 ... 5. Replace the Broken mirrorlist in CentOS-SCLo-scl.repo and CentOS-SCLo-scl-rh.repo with Aliyun baseurls 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. Install the PHP/Apache Frontend 1 [root@zabbix ~]# yum -y install zabbix-web-mysql-scl zabbix-apache-conf-scl 7. Install the MariaDB Database 1 2 3 [root@zabbix ~]# yum -y install mariadb-server Start the database: [root@zabbix ~]#systemctl enable mariadb --now 8. Create the Zabbix Database 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. Initialize the Database (Import the Schema Required Before Zabbix Runs) 1 2 3 4 5 [root@zabbix ~]# zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix # You will be prompted for the zabbix password [root@zabbix ~]# mysql -uroot MariaDB [(none)]\u0026gt; set global log_bin_trust_function_creators = 0; quit 10. Configure Zabbix\u0026rsquo;s Database Connection 1 2 3 4 [root@zabbix ~]# egrep ^DB /etc/zabbix/zabbix_server.conf DBName=zabbix DBUser=zabbix DBPassword=zabbix # Uncomment and set the password 11. Set the Timezone 1 2 [root@zabbix ~]# tail -1 /etc/opt/rh/rh-php72/php-fpm.d/zabbix.conf php_value[date.timezone] = Asia/Shanghai 12. Start Services (server, agent, httpd, php) and Enable on Boot 1 [root@zabbix ~]# systemctl enable zabbix-server zabbix-agent httpd rh-php72-php-fpm --now 13. Access the Zabbix Web UI at 11.0.1.71/zabbix 14. Zabbix Installation Complete 15. Switch to Chinese and Reconfigure the Font Locate a Windows font and copy it into Zabbix\u0026rsquo;s font directory.\nWhere Windows fonts are stored:\nUpload one of the fonts, then rename it:\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 # the uploaded font [root@zabbix fonts]#rm -f graphfont.ttf # remove the original font [root@zabbix fonts]#ln -s /usr/share/zabbix/assets/fonts/simhei.ttf graphfont.ttf # link to the uploaded font [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":"/en/posts/zabbix-install/","readingTime":3,"tags":["Zabbix","monitoring","CentOS7","installation"],"title":"Installing and Deploying Zabbix","url":"/en/posts/zabbix-install/","wordCount":484},{"categories":["Kubernetes","Operations \u0026 Deployment"],"content":"Planning is as follows:\nMaster node: Hostname: master IP address: 192.168.8.30\nWorker01 node: Hostname: node1 IP address: 192.168.8.31\nWorker02 node: Hostname: node2 IP address: 192.168.8.32\nComplete the following steps on all three nodes, and make sure the clocks of all three nodes are synchronized.\n1. Disable Firewall and 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. Disable Swap 1 2 [root@master ~]# sed -i \u0026#39;/swap/s/^/#/\u0026#39; /etc/fstab [root@master ~]# swapoff -a 3. Configure System Parameters and Load Kernel Modules 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. Install the Container Runtime (Containerd) Kubernetes v1.30 recommends using Containerd as the container runtime:\n1 [root@master ~]# yum -y remove runc Remove the OS-bundled yum repository files:\n1 [root@master ~]# rm -rf /etc/yum.repos.d/Cent*.repo Configure the local yum repository:\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 Configure 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 Install containerd.io:\n1 2 3 4 5 6 7 8 9 10 [root@master ~]# yum install -y containerd.io # Generate and edit the configuration file [root@master ~]# mkdir -p /etc/containerd [root@master ~]# containerd config default \u0026gt; /etc/containerd/config.toml # Edit the config to use the systemd cgroup driver #sed -i \u0026#39;s/SystemdCgroup = false/SystemdCgroup = true/g\u0026#39; /etc/containerd/config.toml # Configure image acceleration (optional) #sed -i \u0026#39;s|registry.k8s.io|registry.aliyuncs.com/google_containers|g\u0026#39; /etc/containerd/config.toml # Start and enable on boot #systemctl enable --now containerd 5. Install and Configure 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 Install the specified version of the Kubernetes components:\n1 2 3 [root@master ~]# yum install -y kubelet-1.30.0 kubeadm-1.30.0 kubectl-1.30.0 --disableexcludes=kubernetes # Start kubelet [root@master ~]# systemctl enable --now kubelet 6. Initialize Kubernetes Note: Once all nodes have completed the configuration above, run the following steps only on the master node.\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 # Configure 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. Join Worker Nodes to the Kubernetes Cluster Run the following commands on the worker nodes. Note: the token and CA certificate hash differ per node — follow the prompt from your own system. The certificate may also expire; if it has, regenerate the join command with 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. Install the Calico Network Plugin on the Master Node 1 2 # Install the Calico network plugin [root@master ~]# kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml Note: For well-known reasons, you may encounter issues downloading the Calico image.\nI have revised that manifest file — download it to the master node and apply it:\nThe file is on Baidu Netdisk. Download link: https://pan.baidu.com/s/1BPCFxv0kE0KWb04Qsgy71g (extraction code: s44k)\n1 [root@master ~]# kubectl apply -f /root/calico-ucloud.yaml After installation, run:\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 If you see the output above, Kubernetes has been installed successfully.\n9. Additional Content Run the following commands on every node:\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 Note:\nkubectl shell completion: The first two lines write kubectl\u0026rsquo;s completion script into /etc/bash_completion.d/. This only works if the bash-completion package is installed on the system first (e.g. yum install -y bash-completion or apt install -y bash-completion); otherwise that directory does not exist and completion will not work. kubectl itself also requires these two commands to actually enable completion (Kubernetes needs this extra configuration). docker alias: After Kubernetes switches to the containerd runtime, container management uses crictl instead of docker. Aliasing docker to crictl here is for users accustomed to the docker command, so they can keep their familiar workflow. If you run # docker images and encounter the following error:\nKubernetes shows the following warning — here is how to resolve it.\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.\nThis warning indicates that the container runtime client you are using (such as crictl or kubelet) is using the default endpoint configuration, which is now deprecated and must be set explicitly. The detailed steps to fix it are as follows:\n1 2 3 4 5 6 7 cat \u0026gt; /etc/crictl.yaml \u0026lt;\u0026lt; EOF runtime-endpoint: unix:///run/containerd/containerd.sock # Choose according to your container runtime image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false pull-image-on-create: false EOF Notes:\nIf you use Containerd, the endpoint is unix:///run/containerd/containerd.sock.\nIf you use CRI-O, the endpoint is unix:///run/crio/crio.sock.\nRemark: Optional.\nIf you installed Kubernetes manually, it uses iptables for data forwarding by default. For large-scale scenarios, it is recommended to switch to IPVS mode. The steps to switch to IPVS mode after installation are as follows:\n1. Install the ipvsadm package on all nodes 2. Load the modules on all nodes 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. Edit the kube-proxy ConfigMap 1 kubectl edit cm kube-proxy -n kube-system In the editor that opens, find and modify the following section:\n1 2 3 4 5 6 7 apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: \u0026#34;ipvs\u0026#34; # change from \u0026#34;iptables\u0026#34; to \u0026#34;ipvs\u0026#34; ipvs: scheduler: \u0026#34;rr\u0026#34; # load balancing algorithm: rr|wrr|lc 4. Force a restart of the kube-proxy Pods Delete all kube-proxy Pods to trigger recreation:\n1 #kubectl delete pods -n kube-system -l k8s-app=kube-proxy 5. Verify the new Pods are running normally 1 2 #kubectl get pods -n kube-system -l k8s-app=kube-proxy # All Pods should be in Running state 6. Verify the IPVS configuration is effective 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":"/en/posts/k8s-v1.30-manual-deploy/","readingTime":6,"tags":["Kubernetes","kubeadm","containerd","calico","v1.30","cluster-deployment"],"title":"Manually Deploying a Kubernetes v1.30 Cluster (via kubeadm)","url":"/en/posts/k8s-v1.30-manual-deploy/","wordCount":1075},{"categories":["Virtualization"],"content":" This article systematically introduces KVM virtualization technology, covering its development history, deployment methods, virtual machine management, network mode configuration, and automated VM installation via command line. All commands have been verified on CentOS/RHEL environments.\nI. History of KVM KVM (Kernel-based Virtual Machine) is the dominant virtualization technology in today\u0026rsquo;s cloud computing landscape. Understanding its development history helps explain why the industry ultimately chose KVM.\nVirtualization technologies can be broadly divided into two categories: proprietary virtualization (e.g., VMware, Microsoft Hyper-V) and open-source virtualization (e.g., Xen, KVM).\nThe early representative of open-source virtualization was Xen. AWS and Alibaba Cloud both adopted Xen in their early days; Citrix\u0026rsquo;s XenServer and the desktop cloud product FusionAccess (corresponding to Citrix XenDesktop) were also based on Xen. Huawei\u0026rsquo;s FusionCompute used Xen prior to version 6.3.\nKey milestones are as follows:\n2008: Red Hat acquired KVM and officially released it in RHEL 5.4 (RHEL 5.0 shipped with Xen). 2010: The OpenStack project was born. Red Hat, together with IBM, Dell, VMware, Cisco, HP, and other vendors, embraced the KVM open-source community and built the ecosystem under the OpenStack Foundation. During the same period, Citrix promoted the CloudStack community. 2014: RHEL 5.11 was released (the end of life for the RHEL 5.x series, which provided 7 years of technical support and free upgrades to KVM). Around 2017: AWS launched KVM-based Nitro platform instances. Alibaba Cloud and numerous global vendors fully embraced KVM. Today, the underlying virtualization technology of major cloud providers is essentially KVM. Huawei\u0026rsquo;s FusionCompute 6.3 also switched from Xen to KVM. In 2019, IBM acquired Red Hat for approximately $34 billion, bringing projects such as Ansible, GlusterFS, and Ceph under its umbrella, further validating the value of this technology path.\nII. What is KVM KVM stands for Kernel-based Virtual Machine. Its core characteristics are:\nNo kernel upgrade required: It directly reuses the system\u0026rsquo;s own kernel, with each virtual machine running as a process. Lightweight virtualization platform: More compact and efficient compared to traditional solutions. Unified management: Both Xen and KVM virtualization platforms provide unified management platforms and command-line tools. III. Deploying KVM 1. Install Required KVM Packages Prerequisite: Enable virtualization support (VT-x) in the physical machine\u0026rsquo;s BIOS (enabled by default on most computers). If installing KVM nested inside VMware, you also need to enable VT-x support in VMware settings.\nRecommended resource allocation: 4 CPU cores, 4 GB RAM.\n1 2 [root@cloud ~]# yum -y groupinstall \u0026#34;Virtualization*\u0026#34; [root@cloud ~]# systemctl restart libvirtd.service 2. Graphical VM Installation This lab uses the graphical tool virt-manager to create and install virtual machines.\nNote: The graphical wizard steps are not shown in screenshots in this article. The focus is on command-line operations; the graphical interface can be completed by following the prompts. Additionally, \u0026ldquo;host machine\u0026rdquo; refers to the machine running KVM virtual machines. This lab performs nested virtualization in VMware, so the host machine here is a VMware virtual machine, not a physical machine.\nFirst, prepare the system installation image: create an /isos directory and copy the CentOS 8.4 ISO file into it.\n1 [root@cloud ~]# mkdir /isos Then launch the graphical management interface:\n1 [root@cloud ~]# virt-manager In the interface, click to create a new virtual machine, select the ISO placed in /isos as the installation source, and follow the wizard to configure CPU, memory, and disk to complete the installation. The disk file of the installed virtual machine is saved by default in the /var/lib/libvirt/images/ directory, named like server1.qcow2:\n1 2 3 [root@cloud ~]# cd /var/lib/libvirt/images/ [root@cloud images]# ls server1.qcow2 3. Creating a Disk During the graphical installation in the previous step, virt-manager automatically creates the disk. Here we introduce the manual method, which will be used later for creating cloud images and adding disks to virtual machines. Use qemu-img to create a qcow2 format disk:\n1 [root@cloud ~]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server1.qcow2 10g Tip: If you want to create a cloud image this way, a smaller capacity means faster creation. You can research how to make the image as small as possible. Typically, the system disk does not require high performance; the real performance bottleneck is on the data disk.\n4. Creating Bridge Mode (Virtual Switch with Uplink) Enter the network configuration directory and copy a bridge configuration based on the existing network interface:\n1 2 [root@cloud ~]# cd /etc/sysconfig/network-scripts/ [root@cloud network-scripts]# cp ifcfg-ens33 ifcfg-br0 Edit the bridge interface 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 Edit the physical interface ifcfg-ens33 to bridge it to br0:\n1 [root@cloud network-scripts]# cat ifcfg-ens33 1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 Reload the network configuration and restart the connections:\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. Creating a Cloud Image Requirement: Create a custom cloud machine image.\nAfter deploying the KVM virtual machine, enter the VM and install some necessary software:\nConfigure a local yum repository Install necessary packages: 1 # yum -y install net-tools vim-enhanced bash-completion cloud-init Edit the /etc/cloud/cloud.cfg configuration file and adjust parameters as needed Delete /etc/ssh/ssh_host* Run init 0 to shut down Finally, compress the image:\n1 [root@cloud images]# qemu-img convert -c -O qcow2 /var/lib/libvirt/images/server1.qcow2 /isos/server1.qcow2 IV. Managing KVM Enter the virsh interactive interface:\n1 [root@cloud ~]# virsh Common virtual machine management commands are as follows.\nList all virtual machines:\n1 2 3 4 [root@cloud ~]# virsh list --all Id Name State ------------------------- - server1 shut off Start / Shut down / Force off / Reboot a virtual machine:\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. Accessing the VM Console Interface To enter a KVM virtual machine, add a virtual console to the VM, enabling console access from the host machine.\nEdit the grub configuration inside the virtual machine:\n1 [root@VM1 ~]# vim /etc/default/grub Append console=ttyS0 to the end of GRUB_CMDLINE_LINUX:\n1 GRUB_CMDLINE_LINUX=\u0026#34;crashkernel=auto resume=UUID=94e88e69-fad6-4d1f-99cf-0ba83cc793b5 rhgb quiet console=ttyS0\u0026#34; Regenerate the grub configuration and reboot:\n1 2 [root@VM1 ~]# grub2-mkconfig -o /boot/grub2/grub.cfg [root@VM1 ~]# reboot Connect to the console from the host machine:\n1 [root@cloud ~]# virsh console server1 The shortcut to exit the console is Ctrl + ].\n7. Adding a Disk to a VM via Configuration (1) Create a disk file:\n1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/disk2.qcow2 5g (2) Add the disk via the configuration file:\n1 [root@cloud ~]# vim /etc/libvirt/qemu/server1.xml Add the following disk section to the configuration:\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; Restart the service and start the virtual machine:\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server1 [root@cloud ~]# virsh console server1 Verify the disk inside the KVM virtual machine:\n1 2 3 [root@localhost ~]# fdisk -l /dev/vda /dev/vdb Comparison: Under Xen virtualization, disk device names are /dev/xvda, /dev/xvdb.\n8. Quickly Creating a KVM Virtual Machine Quickly create a new VM by copying an existing VM\u0026rsquo;s XML configuration:\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 Modify key parameters such as name, uuid, disk path, and MAC address:\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; Restart the service and start the new virtual machine:\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server2 [root@cloud ~]# virsh list --all V. KVM Network Configuration KVM commonly supports three network modes: NAT mode, Host-Only mode, and Bridge mode.\n1. NAT Mode NAT is implemented through iptables address masquerading:\n1 # iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE Or use SNAT to specify the source address:\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 View NAT rules:\n1 [root@cloud ~]# iptables -t nat -nL The default network configuration file is located at /etc/libvirt/qemu/networks:\n1 2 3 [root@cloud networks]# pwd /etc/libvirt/qemu/networks [root@cloud networks]# vim default.xml 2. Host-Only Mode In Host-Only mode, virtual machines can only communicate with the host machine and cannot access external networks.\n3. Enabling KVM Virtual Machines to Access the Internet (1) Add a network interface to the VMware host that can access the Internet.\n(2) Enter the host operating system and configure dual bridges.\nifcfg-br0 (internal network bridge):\n1 2 3 4 5 6 TYPE=Bridge NAME=br0 DEVICE=br0 ONBOOT=yes IPADDR=192.168.8.99 PREFIX=24 ifcfg-br1 (external network bridge, DHCP):\n1 2 3 4 5 TYPE=Bridge NAME=br1 DEVICE=br1 ONBOOT=yes BOOTPROTO=dhcp ifcfg-ens33 bridged to br0:\n1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 ifcfg-ens37 bridged to br1:\n1 2 3 4 5 TYPE=Ethernet NAME=ens37 DEVICE=ens37 ONBOOT=yes BRIDGE=br1 Restart the service and shut down the virtual machines:\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 Modify the network interface of server1 in KVM to bridge to br1, and change the IP acquisition method to DHCP inside VM1:\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 Test external network connectivity:\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. Installing KVM Virtual Machines via Command Line With kickstart automated installation scripts, you can perform unattended batch installation of virtual machines.\n1. Create a Disk 1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server3.qcow2 10g 2. Automated Installation Use virt-install to specify the installation source and kickstart file:\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 Example kickstart configuration file 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 Note: The installation source addresses (192.168.0.107), hostname (server3), and the password hashes after rootpw and user in the above example are for illustration only. Please replace them with your own environment\u0026rsquo;s addresses and generated password ciphertexts in actual use.\n","date":"2026-08-02","description":"","objectID":"/en/posts/kvm-virtualization-guide/","readingTime":10,"tags":["KVM","Virtualization","Linux","CentOS"],"title":"KVM Virtualization from Beginner to Practice: Deployment, Management, and Network Configuration","url":"/en/posts/kvm-virtualization-guide/","wordCount":1988},{"categories":["Container Tech"],"content":"1. Choose the Right Base Image 1 2 3 4 5 6 7 8 9 10 # ❌ Too large FROM ubuntu:22.04 # ~77MB FROM python:3.11 # ~1GB # ✅ Recommended FROM python:3.11-alpine # ~50MB FROM python:3.11-slim # ~130MB # ✅ Smallest FROM gcr.io/distroless/python3 # ~20MB Base image Size ubuntu:22.04 ~77MB python:3.11 ~1GB python:3.11-alpine ~50MB distroless ~20MB 2. Use Multi-Stage Builds The most effective technique: compile in a large image, ship the artifact from a minimal one.\n1 2 3 4 5 6 7 8 9 10 11 # Stage 1: compile FROM golang:1.21 AS builder WORKDIR /app COPY . . RUN go build -o myapp # Stage 2: runtime FROM alpine:3.18 WORKDIR /app COPY --from=builder /app/myapp . CMD [\u0026#34;./myapp\u0026#34;] A Go app goes from ~1GB to ~15MB.\n3. Combine RUN Instructions 1 2 3 4 5 6 7 8 9 10 11 12 # ❌ Multiple layers RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y wget RUN rm -rf /var/lib/apt/lists/* # ✅ Combined into one layer RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends \\ curl \\ wget \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* 4. Clean Up Build Cache 1 2 3 4 5 6 7 8 9 # Python — disable pip cache RUN pip install --no-cache-dir -r requirements.txt # Node.js — clear npm cache RUN npm ci --only=production \u0026amp;\u0026amp; \\ npm cache clean --force # Go — strip debug info RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 5. Use .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 # Check context size docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; 6. Install Only What You Need 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # ❌ Build tools left in the runtime image 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;] # ✅ Build and runtime separated 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. Use \u0026ndash;no-install-recommends 1 2 3 4 5 # ❌ Installs all recommended packages RUN apt-get install -y curl # ✅ Install only what\u0026#39;s required (~50% fewer dependencies) RUN apt-get install -y --no-install-recommends curl 8. Compress Static Assets 1 2 3 4 5 6 7 8 9 10 11 # Minify JavaScript RUN uglifyjs app.js -o app.min.js # Minify CSS RUN cssnano app.css app.min.css # Optimize images RUN find . -name \u0026#34;*.png\u0026#34; -exec optipng {} \\; # Strip debug info from Go binaries RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 9. Use Squash to Merge Layers 1 2 # Merge all layers into one, removing duplicate files from intermediate layers docker build --squash -t myapp:slim . Requires the experimental feature to be enabled in /etc/docker/daemon.json.\n10. Analyze Image Layers 1 2 3 4 5 6 7 # Use dive to analyze each layer\u0026#39;s size and content docker run --rm -it \\ -v /var/run/docker.sock:/var/run/docker.sock \\ wagoodman/dive myapp:latest # Or use docker history docker history myapp:latest Real-World Case: Python App 1 2 3 4 5 6 # ❌ Before optimization (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 # ✅ After optimization (150MB, 87% smaller) 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;] Real-World Case: Node.js App 1 2 3 4 5 6 # ❌ Before optimization (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 # ✅ After optimization (180MB, 84% smaller) 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;] Recommended Tools Tool Purpose dive Analyze each layer\u0026rsquo;s content and size docker-slim Automatically shrink images hadolint Dockerfile static analysis trivy Container security scanning ","date":"2026-07-30","description":"10 proven techniques to reduce Docker image size by over 90%.","objectID":"/en/posts/dockerfile-smaller-image/","readingTime":4,"tags":["Docker","Dockerfile","Image Optimization","Containerization"],"title":"Docker Image Too Large? 10 Tips to Shrink It by 90%","url":"/en/posts/dockerfile-smaller-image/","wordCount":716},{"categories":["Container Tech"],"content":"1. Order Instructions to Leverage Cache Put rarely-changing instructions first, frequently-changing ones last. Once a layer\u0026rsquo;s cache is invalidated, every subsequent layer is rebuilt.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ Wrong: reinstall dependencies on every code change FROM node:18-alpine WORKDIR /app COPY . . # code changes often RUN npm ci --production # deps rarely change CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # ✅ Right: reinstall deps only when they change FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # dep definitions (rarely change) RUN npm ci --production # install deps (rarely change) COPY . . # source code (changes often) CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 # Check cache hits docker build -t myapp . 2\u0026gt;\u0026amp;1 | grep -E \u0026#34;(CACHED|Using cache)\u0026#34; 2. Use .dockerignore to Shrink the Build Context docker build sends the entire directory to the Docker daemon, including .git, node_modules, and other files you don\u0026rsquo;t need.\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 # Check context size docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; # Before: Sending build context to Docker daemon 1.2GB # After: Sending build context to Docker daemon 50MB 3. Use Lightweight Base Images Base image Size Notes ubuntu:22.04 ~77MB Full Ubuntu python:3.11 ~1GB Full Python python:3.11-slim ~130MB Stripped down python:3.11-alpine ~50MB Alpine-based 1 2 3 4 5 6 7 8 # ❌ Too large FROM python:3.11 # 1GB # ✅ Recommended FROM python:3.11-slim # 130MB # ✅ Smallest (watch out for musl compatibility) FROM python:3.11-alpine # 50MB Alpine uses musl instead of glibc, which can break some software. If you hit issues, fall back to slim.\n4. Combine RUN Instructions to Reduce Layers Every RUN creates a new layer — more layers means slower builds and larger images.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ 5 layers 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 layer 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 # Check image layer count docker history myapp:latest 5. Use Multi-Stage Builds Build in one stage, run in another, and copy only what\u0026rsquo;s needed.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Stage 1: build 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 . # Stage 2: runtime (only ~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;] Approach Image size Build time Single-stage ~1.2GB 5 min Multi-stage ~15MB 2 min 6. Use BuildKit to Speed Up Builds BuildKit supports parallel builds and advanced caching.\n1 2 3 4 5 6 7 # Enable BuildKit export DOCKER_BUILDKIT=1 # Or enable it permanently in /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 # Use cache mounts (a BuildKit feature) # syntax=docker/dockerfile:1 FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # Cache mount avoids re-downloading deps every build 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 # Check whether BuildKit is enabled docker build --progress=plain -t myapp . 2\u0026gt;\u0026amp;1 | head -5 7. Use Cache Import/Export In CI/CD, the cache is lost after every build. Cache import/export keeps it across builds.\n1 2 3 4 5 6 7 8 9 10 11 # Export to a local directory docker build \\ --cache-from type=local,src=/tmp/cache \\ --cache-to type=local,dest=/tmp/cache \\ -t myapp . # Use a registry as cache backend 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 example - 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. Install Dependencies in Parallel 1 2 3 4 5 6 7 8 9 10 11 12 # Node.js — npm ci is faster than npm install RUN npm ci --production # Python — disable pip cache RUN pip install --no-cache-dir -r requirements.txt # Go — download modules in parallel RUN go mod download # Rust — use cargo-chef COPY --from=chef /app/recipe.json recipe.json RUN cargo chef cook --release --recipe-path recipe.json 9. Clean Up Build Cache and Temp Files Clean up in the same RUN instruction to avoid leaving files in earlier layers.\n1 2 3 4 5 6 7 8 9 10 11 12 # ✅ Right: clean up in the same layer 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/* # ❌ Wrong: cleanup in another layer — files still exist RUN apt-get update \u0026amp;\u0026amp; apt-get install -y build-essential RUN make \u0026amp;\u0026amp; make install RUN apt-get clean # too late! earlier layers still contain the files Per-language cleanup commands:\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. Pin a Specific Version Tag 1 2 3 4 5 6 7 8 # ❌ latest may change, cache is unpredictable FROM node:latest # ✅ Specific version FROM node:18.17.0-alpine3.18 # ✅ Safest: use the SHA256 digest FROM node:18.17.0-alpine3.18@sha256:abc123... How Layers Work 1 2 # View image layer history docker history \u0026lt;image-name\u0026gt; Instructions that create new layers: FROM, RUN, COPY, ADD\nInstructions that don\u0026rsquo;t create new layers (just metadata): CMD, ENTRYPOINT, ENV, EXPOSE, WORKDIR, USER, LABEL, ARG, VOLUME\n1 2 3 4 5 6 7 8 9 10 11 # This creates 4 layers FROM node:18-alpine # layer 1 RUN apk add --no-cache curl # layer 2 COPY package.json . # layer 3 RUN npm install # layer 4 # These instructions add no layer WORKDIR /app # no layer ENV NODE_ENV=production # no layer EXPOSE 3000 # no layer CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # no layer Before / After Comparison 1 2 3 4 5 6 7 8 9 # ❌ Before optimization (8 min, 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 # ✅ After optimization (1.5 min, 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;] Metric Before After Improvement Build time 8 min 1.5 min 81% ⬇️ Image size 1.2GB 150MB 87% ⬇️ Context size 500MB 50MB 90% ⬇️ Cache hit rate 10% 90% 80% ⬆️ Monitoring and Debugging 1 2 3 4 5 6 7 8 9 10 11 # Show detailed build time time docker build -t myapp . # BuildKit detailed progress DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp . # Analyze image layers docker history myapp:latest # Use dive for deep layer analysis dive myapp:latest ","date":"2026-07-30","description":"10 proven tips — from cache strategy to multi-stage builds — to dramatically cut Docker build time.","objectID":"/en/posts/dockerfile-faster-build/","readingTime":6,"tags":["Docker","Dockerfile","Performance","CI/CD"],"title":"How to Make Dockerfile Builds Faster: 10 Practical Optimization Tips","url":"/en/posts/dockerfile-faster-build/","wordCount":1259},{"categories":["Container Tech"],"content":"1. Basic Syntax The syntax is almost identical, but the behavior is very different:\n1 2 3 4 5 # COPY - pure file copy, no extra processing COPY [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; # ADD - supports auto-extraction and remote URLs, but with unpredictable behavior ADD [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; 2. Auto-Extracting Archives ADD automatically extracts local archives, while COPY does not:\n1 2 3 4 5 6 7 # ADD auto-extracts .tar.gz ADD app.tar.gz /opt/app/ # Result: contents are extracted into /opt/app/ # COPY only copies the file itself COPY app.tar.gz /opt/app/ # Result: /opt/app/app.tar.gz remains an archive Supported archive formats: .tar, .tar.gz / .tgz, .tar.bz2, .tar.xz\nIf you want to copy an archive without extracting it, you must use COPY.\n3. Remote URL Downloads ADD can download files directly from a URL; COPY does not:\n1 2 3 4 5 # ADD can fetch a remote file (not recommended) ADD https://example.com/app.tar.gz /opt/ # COPY does not support URLs COPY https://example.com/app.tar.gz /opt/ # Error! Prefer RUN + curl over ADD for remote downloads: ADD downloads don\u0026rsquo;t benefit from build cache, can\u0026rsquo;t handle errors, and don\u0026rsquo;t verify integrity.\n1 2 3 4 # Recommended: 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. Auto-Creating Directories Both automatically create missing intermediate directories:\n1 2 3 4 5 6 # If /etc/myapp/config/ doesn\u0026#39;t exist, it will be created automatically COPY app.conf /etc/myapp/config/ ADD app.conf /etc/myapp/config/ # No need for manual mkdir # RUN mkdir -p /etc/myapp/config ← unnecessary 5. Build Cache Behavior 1 2 3 4 5 # COPY - strictly matches file content; cache is only invalidated when content changes COPY config/app.conf /etc/app/ # ADD - cache stays valid as long as the archive content is the same ADD app.tar.gz /opt/app/ Scenario COPY ADD File content changes Cache invalidated Cache invalidated File metadata changes Cache invalidated Cache invalidated Same archive content N/A Cache valid 6. Feature Comparison Feature COPY ADD Copy local files ✅ ✅ Copy directories ✅ ✅ Wildcard support ✅ ✅ Auto-create directories ✅ ✅ Auto-extract archives ❌ ✅ Remote URL support ❌ ✅ Clarity of intent ✅ High ❌ Low Recommended ✅ Default ⚠️ Specific cases 7. When to Use Each Use COPY for:\n1 2 3 4 5 6 7 8 9 # Copy config files COPY nginx.conf /etc/nginx/nginx.conf # Copy application code COPY src/ /app/src/ COPY package.json /app/ # Copy a binary from a multi-stage build COPY --from=builder /app/build/app /usr/local/bin/ Use ADD only when auto-extraction is needed:\n1 2 3 4 5 # Reasonable use of ADD ADD node-v18.17.0-linux-x64.tar.gz /usr/local/ # Don\u0026#39;t use ADD for regular files ADD app.conf /etc/app/ # ← use COPY instead 8. Best Practices 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # Rule 1: default to COPY COPY requirements.txt /app/ # ✅ ADD requirements.txt /app/ # ❌ unnecessary # Rule 2: use ADD only when extraction is needed ADD node-v18.tar.gz /usr/local/ # ✅ extraction needed # Rule 3: use RUN + curl for remote files 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/ # ❌ # Rule 4: use .dockerignore to shrink the build context # .dockerignore # .git # node_modules # *.md # .env # Rule 5: order COPY statements to leverage cache COPY package.json package-lock.json ./ # dependencies first (rarely change) RUN npm ci --production COPY . . # source code last (changes often) 9. Common Pitfalls 1 2 3 4 5 6 7 8 9 10 # Pitfall 1: unexpected auto-extraction ADD app.tar.gz /opt/app/ # You expected a file copy, but got an extracted directory # Pitfall 2: remote files can\u0026#39;t be cached ADD https://github.com/user/repo/releases/download/v1.0/app.tar.gz /tmp/ # Every build re-downloads # Pitfall 3: ownership issues COPY --chown=app:app src/ /app/src/ # specify the owner explicitly 10. Decision Tree 1 2 3 4 5 6 7 Need to copy a file? ├── Local archive that needs extraction? │ └── Use ADD ├── Local regular file? │ └── Use COPY └── Remote file? └── Use RUN + curl ","date":"2026-07-30","description":"COPY and ADD can both copy files, but their behaviors differ greatly. This article explains the difference through side-by-side comparisons and examples.","objectID":"/en/posts/dockerfile-copy-vs-add/","readingTime":4,"tags":["Docker","Dockerfile","Containerization"],"title":"COPY vs ADD in Dockerfile: A Detailed Comparison","url":"/en/posts/dockerfile-copy-vs-add/","wordCount":697},{"categories":["Tech Tutorials"],"content":"1. Requirements OS: CentOS 7 / 8 / Stream 9 Node.js: v18 or higher RAM: 2 GB or more recommended Network: Access to the Anthropic API 2. Install Node.js 1 2 3 4 5 6 7 8 9 # Add the NodeSource repository curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash - # Install Node.js sudo yum install -y nodejs # Verify node -v npm -v If the Node.js version is too old, use nvm to manage versions:\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. Install Claude Code 1 2 3 4 5 # Global install npm install -g @anthropic-ai/claude-code # Verify claude --version Two ways to fix permission issues:\n1 2 3 4 5 6 7 8 # Option 1: sudo sudo npm install -g @anthropic-ai/claude-code # Option 2: change the npm global prefix (recommended) 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. Configure the API Key 1 2 3 4 5 6 # Option 1: environment variable (recommended) echo \u0026#39;export ANTHROPIC_API_KEY=\u0026#34;your-api-key-here\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc # Verify echo $ANTHROPIC_API_KEY Option 2: just run claude — it will prompt you for the API key on first launch.\n5. Basic Usage 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Start an interactive session cd /path/to/your/project claude # Ask a question directly (non-interactive) claude -p \u0026#34;Write a Python script that counts the number of files in the current directory\u0026#34; # Analyze code cat main.py | claude -p \u0026#34;Analyze potential issues in this code\u0026#34; # View or edit configuration claude config # Update to the latest version claude update Command Description claude Start an interactive session claude -p \u0026quot;question\u0026quot; Ask directly in non-interactive mode claude config View or edit configuration claude update Update to the latest version 6. Network Troubleshooting If connecting to the Anthropic API times out:\n1 2 3 4 5 6 # Configure a proxy export https_proxy=\u0026#34;http://your-proxy:port\u0026#34; export http_proxy=\u0026#34;http://your-proxy:port\u0026#34; # Test connectivity curl -s https://api.anthropic.com/ | head -5 7. Security Tips 1 2 3 4 5 6 7 8 9 # Never hard-code the API key — always use an environment variable 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 written in code # ❌ # Restrict file permissions chmod 600 ~/.bashrc # Check for leaks 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":"Deploy Claude Code on CentOS from scratch — covers Node.js installation, API key configuration, and common troubleshooting.","objectID":"/en/posts/centos-deploy-claude-code/","readingTime":2,"tags":["Claude Code","CentOS","AI","Deployment"],"title":"CentOS Deployment Guide for Claude Code: Full Walkthrough","url":"/en/posts/centos-deploy-claude-code/","wordCount":399},{"categories":null,"content":"flannel in Action: Building Docker Cross-Host Container Networks from Scratch I. Network Environment Planning etcd host IP: 192.168.8.88 hostname: docker-network Host1 IP: 192.168.8.188 hostname: web01 Host2 IP: 192.168.8.99 hostname: web02 II. etcd Host Configuration Step 1: Download and Extract etcd Download etcd-v3.6.0-linux-amd64.tar.gz package from github.com Copy the package to the etcd host /root directory and extract: 1 [root@docker-network ~]# tar -zxvf etcd-v3.6.0-linux-amd64.tar.gz Step 2: Install etcd Executable Files 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/ Step 3: Create Data Directory 1 [root@docker-network ~]# mkdir /var/lib/etcd Step 4: Configure and Start etcd Service Create etcd.service file: 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 Reload system service configuration and start etcd: 1 2 3 [root@docker-network ~]# systemctl daemon-reload [root@docker-network ~]# systemctl enable etcd.service [root@docker-network ~]# systemctl restart etcd.service Verify etcd listening status: 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 Step 5: Add flannel Network Configuration to etcd 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; The above content is all on one line. Verify that the configuration was written successfully\n1 etcdctl --endpoints http://192.168.8.88:2379 get /coreos.com/network/config Configuration description:\nNetwork: Flannel address pool SubnetLen: Subnet mask length assigned to a single host\u0026rsquo;s docker0 (24 bits means 255.255.255.0) SubnetMin/SubnetMax: Allocatable subnet range (example is 172.16.1.0/24 to 172.16.10.0/24) Backend: Data forwarding mode (vxlan supports cross-host communication) Step 6: Disable Firewall 1 2 [root@docker-network ~]# systemctl disable firewalld [root@docker-network ~]# systemctl stop firewalld III. web01 Host Configuration (Host1: 192.168.8.188) Step 1: Download and Extract flannel Download flannel-v0.26.7-linux-amd64.tar.gz to the host Extract the file: 1 [root@web01 ~]# tar -zxvf flannel-v0.26.7-linux-amd64.tar.gz Step 2: Install flannel Executable Files 1 [root@web01 ~]# cp -p flanneld mk-docker-opts.sh /usr/local/bin/ Step 3: Configure and Start flannel Service Create flanneld.service file: 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 Parameter description:\n--iface=192.168.8.188: Specify the local IP for connecting to etcd --etcd-endpoints: etcd server address Reload service configuration and start flannel:\n1 2 3 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl enable flanneld.service [root@web01 ~]# systemctl restart flanneld.service Step 4: Verify flannel Network Interface 1 [root@web01 ~]# ifconfig Should display flannel.1 interface (example output):\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 Step 5: Modify Docker Configuration File View subnet environment variables generated by flannel: 1 [root@web01 ~]# cat /run/flannel/subnet.env Example output:\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 Example output:\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; Modify docker service configuration: 1 [root@web01 ~]# vim /usr/lib/systemd/system/docker.service Add to the [Service] section:\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 Restart docker service: 1 2 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl restart docker.service Step 6: Verify Docker Network Create nginx container and enter it: 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;container_id\u0026gt; /bin/bash View container IP (example is 172.16.5.3): 1 2 root@container_id:/# ifconfig eth0: inet 172.16.5.3 netmask 255.255.255.0 IV. web02 Host Configuration (Host2: 192.168.0.208) Step Description Configuration steps are identical to web01, only need to modify the following parameters: --iface=192.168.8.99 (local IP) Docker container IP example: 172.16.5.2 (automatically assigned by flannel) V. Experiment Verification Ping web02 container IP (example: 172.16.9.2) from web01 container:\nCreate a container in web02 and view the container IP address\nResult: Inter-container communication successful, flannel network configuration complete.\nAppendix: etcd Network Configuration Query 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":"/en/posts/flannel-network-lab/","readingTime":4,"tags":["flannel","Docker","networking","etcd"],"title":"flannel in Action: Building Docker Cross-Host Container Networks from Scratch","url":"/en/posts/flannel-network-lab/","wordCount":787},{"content":"Harbor Active-Standby Deployment Environment Setup Operating System: CentOS 8.4 64bit\nHarbor Version: harbor-offline-installer-v2.11.1.tgz\nDocker Compose: docker-compose-linux-x86_64\nRole Hostname IP Address Primary Harbor Harbor01 192.168.8.161 Standby Harbor Harbor02 192.168.8.162 Docker Host web01 192.168.8.188 Harbor is an open-source enterprise Docker Registry project by VMware, built on the official Docker Registry with added features like management UI, role-based access control (RBAC), AD/LDAP integration, and audit logging. Each Harbor component runs as a Docker container, deployed using Docker Compose.\nPart 1: Deploy Primary Harbor (HTTP Mode, No Certificates) This method is suitable for internal networks where Harbor is accessed directly via HTTP without SSL certificates.\n1. Install Docker 1 2 3 4 5 6 7 8 9 10 11 12 13 # Remove existing yum repo files rm -rf /etc/yum.repos.d/* # Download repo file from Alibaba Cloud mirror curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo # Install 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 # Start Docker and enable on boot systemctl enable docker.service --now 2. Configure Docker Mirror 1 vim /etc/docker/daemon.json Enter the following content:\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. Install Docker Compose and Harbor 1 2 3 4 5 6 7 8 9 10 # Place docker-compose and harbor offline packages in /data ls /data # docker-compose-linux-x86_64 harbor-offline-installer-v2.11.1.tgz # Install docker-compose cp /data/docker-compose-linux-x86_64 /usr/local/bin/docker-compose chmod a+x /usr/local/bin/docker-compose # Extract harbor tar -zxvf /data/harbor-offline-installer-v2.11.1.tgz -C /usr/local/ 4. Modify Harbor Configuration 1 2 3 cd /usr/local/harbor/ cp harbor.yml.tmpl harbor.yml vim harbor.yml Modify the following key settings:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 hostname: 192.168.8.161 http: port: 80 # Comment out the entire https section (not needed for HTTP mode) #https: # port: 443 # certificate: /your/certificate/path # private_key: /your/private/key/path harbor_admin_password: Harbor12345 data_volume: /images Leave all other settings at their defaults.\n5. Run Installation 1 ./install.sh After installation, access http://192.168.8.161 in your browser and log in with admin / Harbor12345.\n6. Create Users and Projects Create user1 in the Harbor Web UI:\nLog in as user1:\nCreate a new project (e.g., openssh) as user1:\n7. Test Image Push and Pull On the Docker host (web01):\n1 2 3 4 5 6 7 8 9 10 11 # Log in to Harbor docker login 192.168.8.161 Username: user1 Password: # Login Succeeded # Tag the image docker tag nginx:latest 192.168.8.161/openssh/nginx:latest # Push to Harbor docker push 192.168.8.161/openssh/nginx:latest After successful push, the image appears in Harbor:\n1 2 3 # Remove local image and pull again docker image rm -f 192.168.8.161/openssh/nginx:latest docker pull 192.168.8.161/openssh/nginx:latest Harbor shows download count +1 after pull:\nPart 2: Deploy Standby Harbor The standby deployment is identical to the primary, just change the hostname to the standby IP.\n1 vim /usr/local/harbor/harbor.yml 1 hostname: 192.168.8.162 After deployment, access http://192.168.8.162 and create user2:\nLog in to standby Harbor as user2:\nPart 3: Configure Active-Standby Replication 1. Create Replication Endpoint on Primary Log in to primary Harbor as admin, go to Administration → Registries, and create a new endpoint:\nEnter standby information:\nEndpoint URL: http://192.168.8.162 Access ID / Access Secret: user2 credentials Creation successful:\n2. Create Replication Policy Go to Administration → Replications, create a new replication policy:\nDestination Registry: Select Harbor02 Mode: Event-Based (or other modes) 3. Verify Sync Results Push more images from Docker host to primary:\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 Check primary images:\nCheck standby images (auto-synced):\nThe standby shows synced images from primary, confirming active-standby replication is working.\nNote: Images that existed on primary before configuring replication need manual sync.\nPart 4: Enable HTTPS (Self-Signed Certificate) For production or public network access, HTTPS is recommended. Here\u0026rsquo;s the complete self-signed certificate workflow.\n1. Generate CA and Server Certificates On the Harbor server:\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 # Generate CA private key openssl genrsa -out ca.key 4096 # Generate CA certificate (valid for 10 years) openssl req -x509 -new -nodes -sha512 -days 3650 \\ -subj \u0026#34;/C=CN/CN=HarborRootCA\u0026#34; \\ -key ca.key \\ -out ca.crt # Generate server private key openssl genrsa -out harbor.key 4096 # Generate server certificate request (CN = Harbor IP or domain) openssl req -sha512 -new \\ -subj \u0026#34;/C=CN/CN=192.168.8.161\u0026#34; \\ -key harbor.key \\ -out harbor.csr 2. Configure Certificate Extensions Create v3.ext file with SAN (Subject Alternative Name):\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 must match Harbor\u0026rsquo;s actual access address, otherwise Docker client will report certificate domain mismatch errors.\n3. Sign Server Certificate 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. Enable HTTPS in Harbor Configuration 1 vim /usr/local/harbor/harbor.yml Uncomment the https section and update certificate paths:\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 must match IP.1 in v3.ext.\n5. Redeploy Harbor 1 2 3 4 cd /usr/local/harbor/ docker-compose down ./prepare docker-compose up -d Warning: Do NOT use docker-compose down -v, as -v deletes named volumes, causing loss of Harbor database and user data.\nAccess via https://192.168.8.161 after deployment.\n6. Configure Docker Clients to Trust Certificate Execute on every machine that needs to push/pull images:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Create certificate directory (name must be Harbor IP or domain) mkdir -p /etc/docker/certs.d/192.168.8.161 # Copy CA certificate to the directory # Method 1: scp from Harbor server scp root@192.168.8.161:/data/cert/ca.crt /etc/docker/certs.d/192.168.8.161/ # Method 2: Manually copy the ca.crt file content # Restart Docker systemctl restart docker # Test login docker login 192.168.8.161 # Enter admin and password, \u0026#34;Login Succeeded\u0026#34; indicates success Part 5: FAQ Q: Can I switch between HTTP and HTTPS modes?\nYes. Modify the https section in harbor.yml, then re-run ./install.sh. Switching from HTTP to HTTPS requires generating certificates first; switching back to HTTP requires commenting out the https section.\nQ: Docker client reports \u0026ldquo;x509: certificate relies on legacy Common Name field\u0026rdquo; when logging into HTTPS Harbor?\nThis means the certificate lacks SAN (Subject Alternative Name). Regenerate the certificate ensuring v3.ext contains IP.1=\u0026lt;Harbor IP\u0026gt; and sign with -extfile v3.ext.\nQ: How to troubleshoot replication failures?\nCheck network connectivity between primary and standby, verify the target registry credentials, and ensure the user has write permission to the target project.\n","permalink":"https://LMDAWN1127.github.io/en/posts/harbor-master-slave/","summary":"Complete guide to deploying Harbor enterprise Docker registry with active-standby architecture, HTTP/HTTPS modes, self-signed certificates, and cross-host replication.","title":"Harbor Active-Standby Deployment: From Installation to HTTPS and Replication"},{"content":"I. OpenStack Core Components and Their Roles 1. HORIZON — Unified Web Dashboard Service Core function: Provides a Web-based Dashboard graphical management console for cloud administrators and tenants to visually allocate and monitor cloud resources. 2. NOVA — Compute Resource Management Service Core function: Manages and configures virtualized compute resources in the cloud environment (nova-compute). Role division: Controller node: Runs scheduling services such as nova-api, nova-scheduler, and nova-conductor. When a user requests a new cloud instance, nova-api receives and parses the request, and nova-scheduler selects the best compute node for load scheduling based on policy. Compute node: The worker node that actually hosts running cloud instances (supports heterogeneous hypervisors such as KVM, VMware ESXi, and Xen). High availability and performance bottlenecks of the controller node: The controller node hosts all API gateways, databases, and message queues, creating HA and performance pressure; 3. GLANCE — Image Service Core function: Responsible for discovery, registration, retrieval, and storage management of OS images. Common disk image formats: Common KVM format: qcow2 (supports thin provisioning and copy-on-write, COW); Common VMware format: vmdk. Deployment recommendation: Usually co-located and integrated on the controller node for high availability. 4. SWIFT — Object Storage Service Core function: Provides a highly available, distributed, eventually-consistent object storage service (Object Storage), purpose-built for static unstructured data, cloud instance system images, and full data backups. 5. NEUTRON — Software-Defined Networking (SDN) Core function: Provides network-as-a-service in the cloud environment, managing virtual routers, switches, security groups, floating IPs, and cross-node traffic connectivity. Network technologies: Fully supports VLAN, VXLAN, Geneve, and distributed virtual routing (DVR); Hardware SDN mode: Production environments typically deploy 2 dedicated network nodes to build an active-active or active-standby cluster. 6. CINDER — Block Storage Service Core function: Provides persistent block storage (cloud disks) for cloud instances, with dynamic attach, detach, snapshot, and resize. Underlying backend integration: Integrates with traditional storage arrays: Huawei OceanStor, IBM, EMC, and other high-end hardware storage; Integrates with open-source distributed storage: Ceph RBD, GlusterFS, etc. Underlying mechanism: When a user requests a cloud disk, Cinder passes the instruction down to the backend storage system—essentially carving and mapping out a LUN within physical or distributed storage. Comparison: Object storage (e.g., Baidu Netdisk, Swift, S3) cannot be formatted and mounted directly as a block device for a cloud instance\u0026rsquo;s filesystem, whereas a Cinder block device can be formatted and mounted directly. 7. HEAT — Orchestration Service Core function: Based on declarative templates (HOT templates or AWS CloudFormation format), it automates one-click batch deployment of multiple VMs, database clusters, network topologies, and monitoring policies via graphical orchestration or scripts. 8. CEILOMETER — Telemetry \u0026amp; Monitoring Service Core function: Collects performance metrics, uptime, and usage of physical and virtual resources. Public cloud scenario: Records API call volume, network egress, and compute hours, integrating with the billing system; Private cloud scenario: Monitors tenant quota levels, identifies over-use or idle resources, and supports capacity planning. 9. KEYSTONE — Unified Identity \u0026amp; Authorization Service Core function: The security cornerstone of OpenStack, responsible for tenant (Project), user (User), role (Role), service catalog, and Token authentication across the platform. Enterprise identity federation: Can seamlessly integrate with the enterprise\u0026rsquo;s existing Windows Active Directory (AD) or Linux OpenLDAP to deliver enterprise-grade unified-account single sign-on (SSO); Workflow approval and privilege isolation (Huawei Cloud as an example): Supports multi-level cloud instance request and approval workflows (up to 5 approval levels); Enforces the \u0026ldquo;separation of three powers\u0026rdquo; security principle (security officer, system administrator, and auditor manage with separated privileges); Supports integration with the customer\u0026rsquo;s in-house OA/BPM approval-flow systems. Quick Reference Table of Core Components Component Code Name Core Service Function Horizon Dashboard Unified Web Graphical Management Console Nova Compute Compute Resource Lifecycle \u0026amp; Instance Scheduling Glance Image VM Image Storage \u0026amp; Version Registration Swift Object Large-Scale Unstructured Object Storage Service Neutron Network Software-Defined Networking (SDN), Routing \u0026amp; Security Group Management Cinder Block Persistent Block Storage (Cloud Disk) Management for Instances Heat Orchestration Infrastructure-as-Code (IaC) Multi-Resource Stack Orchestration Ceilometer Metering Resource Usage Statistics, Monitoring Metering \u0026amp; Quota Analysis Keystone Identity Unified Identity Authentication, Token \u0026amp; Service Catalog Management II. OpenStack Deployment Planning (Victoria Edition) 1. Deployment Method Comparison Open-source manual deployment: Compile and configure each component individually—tedious and complex, usually for deep learning of underlying mechanisms; Tool-based automated deployment: Ansible deployment (e.g., OpenStack-Ansible, Kolla-Ansible); Packstack deployment: Red Hat\u0026rsquo;s open-source, Puppet-based automated rapid-delivery tool (used in this walkthrough); 2. Cluster Node Specifications and Network Planning (1) Controller Node (1 unit) Hosted components: API services: nova-api, cinder-api, glance-api, neutron-server, etc.; Underlying base services: MariaDB / MySQL, RabbitMQ message queue, Memcached, Keystone; Network and management services: external routing on the network node, local YUM cache, and Chrony NTP server. System requirements: CPU: 4 vCPU minimum Memory: 4GB minimum Disk: 100GB available disk space NICs: 3 network cards (NIC 1 in bridged mode for Internet access; NIC 2 and NIC 3 in host-only mode for internal traffic) IP address planning: Internal management IP: 192.168.100.10 External access IP: 192.168.10.10 (2) Compute Node (1 unit) Hosted components: nova-compute, and the network L2 Agent (Open vSwitch Agent) System requirements: CPU: 4 vCPU minimum Memory: 8GB minimum (4GB at least) Disk: 100GB disk space NICs: 3 network cards (all in host-only mode) IP address planning: Compute: 192.168.100.11 (3) YUM Repository Server + Chrony NTP Server (1 unit) System requirements: CPU: 2 vCPU Memory: 2GB NICs: 2 cards (one bridged to the Internet for public time sync and upstream repos, the other host-only to serve internal nodes) IP address planning: External IP: 192.168.10.11 Internal IP: 192.168.100.20 Environment downgrade note: If your personal computer is short on hardware, you can compress down to a minimum of 2 VMs (the controller node also acts as the YUM source and NTP server, plus 1 compute node). Note: the system clocks of all nodes must stay highly consistent—clock drift must not exceed 5 minutes, otherwise Keystone handshakes and message-queue communication will fail entirely.\nIII. Full Automated Deployment Walkthrough Phase 1: YUM Repository Server and Chrony NTP Server Setup 1. Configure Network Interfaces 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. Configure the internal communication 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. Configure the Internet-facing NIC ens192 (for syncing public 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 # Restart the NICs to apply the configuration [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. Configure Local CD-ROM YUM Repository 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/ # Mount the OS installation ISO mount /dev/cdrom /media/ # Write the local CD-ROM repository config file [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. Configure Chrony NTP Server Because OpenStack is extremely strict about inter-node clock synchronization, configure the time source first:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # Edit /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 # Restart and verify the time sync service [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 # Disable the firewall and 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. Build an httpd-based Local Web YUM Repository Publish the CentOS 8 and OpenStack Victoria software repositories as an HTTP mirror source for all cluster nodes to pull from.\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. Install and start the Apache web service [root@Cloud ~]# yum -y install httpd [root@Cloud ~]# systemctl enable httpd --now # 2. Create directories and mount the image files [root@Cloud ~]# mkdir /isos [root@Cloud ~]# mkdir -p /var/www/html/centos8 [root@Cloud ~]# mkdir -p /var/www/html/openstack # Mount the base system CD and the OpenStack Victoria installation image [root@Cloud ~]# mount /dev/cdrom /var/www/html/centos8/ [root@Cloud ~]# mount /isos/26-CentOS8-4-OSP-Victoria.iso /var/www/html/openstack/ # 3. Persist the mounts in /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. Configure the HTTP YUM repository files [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. Distribute the prepared repo files to each controller and compute node [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/ Phase 2: System Environment Initialization for Each Cluster Node 1. Controller Node Initialization (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. Configure the management network 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. Configure the external network 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. Configure the Chrony NTP client [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. Set the hostname [root@Controller ~]# hostnamectl set-hostname Controller # 5. Disable the firewall and 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. Configure cluster /etc/hosts resolution [root@Controller ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 7. Clean the default system repo cache [root@Controller ~]# cd /etc/yum.repos.d [root@Controller yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ 2. Compute Node Initialization (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. Configure the management network [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. Configure the Chrony NTP client [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. Set the hostname [root@Compute ~]# hostnamectl set-hostname Compute # 4. Disable the firewall and 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. Configure cluster hosts resolution [root@Compute ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 6. Clean the default system repo [root@Compute ~]# cd /etc/yum.repos.d [root@Compute yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ ⚠️ Key checkpoint: It is recommended to shut down all VMs at this point and take a unified snapshot for quick rollback if something goes wrong.\nPhase 3: Packstack Automated Answer-File Deployment 1. Install the Packstack Deployment Tool Run on the controller node (Controller):\n1 [root@Controller ~]# yum -y install openstack-packstack 2. Generate the Default Answer File 1 [root@Controller ~]# packstack --gen-answer-file=/root/answers.txt Networking note: OpenStack Victoria supports both OVN (Open Virtual Network) and the traditional OVS (Open vSwitch).\n3. Customize the Answer File Parameters 1 2 # Replace the auto-detected external IP with the internal management 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 Edit /root/answers.txt and review and modify the following core configuration items:\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. Specify the compute node cluster IP list (comma-separated) CONFIG_COMPUTE_HOSTS=192.168.100.11 # 2. Set the Keystone admin root initial password CONFIG_KEYSTONE_ADMIN_PW=000000 # 3. Disable the Demo tenant network (production hardening) CONFIG_PROVISION_DEMO=n # 4. Enable Heat orchestration component support CONFIG_HEAT_INSTALL=y # 5. Neutron core driver and network type configuration (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 external network bridge binding configuration (associates the physical external NIC ens224) CONFIG_NEUTRON_OVS_BRIDGE_MAPPINGS=extnet:br-ex CONFIG_NEUTRON_OVS_BRIDGE_IFACES=br-ex:ens224 # 7. Preset storage quota capacity CONFIG_SWIFT_STORAGE_SIZE=20G CONFIG_CINDER_VOLUMES_SIZE=50G ⚠️ Key checkpoint: After modifying the answer file, it is recommended to shut down all VMs again and take a snapshot.\n4. Execute One-Click Automated Deployment After powering on, run the deployment command on the controller node:\n1 [root@Controller ~]# packstack --answer-file=/root/answers.txt The deployment script uses Puppet to automatically complete software-source configuration, database table creation, message-queue cluster setup, certificate generation, and coordinated integration testing of all components across all nodes. When finished, it prints the Dashboard login URL and the keystonerc_admin environment-variable file path to the terminal.\nIV. Linux System Log Management and OpenStack Troubleshooting 1. Linux Rsyslog Log Levels and Configuration Architecture In Linux systems, logs from the low-level system and most services are centrally managed by the rsyslog daemon.\nView the config file: 1 2 rpm -qc rsyslog # Core main config file: /etc/rsyslog.conf Log Rule Syntax Analysis 1 facility.level(priority) log storage path Common configuration examples:\n1 2 3 4 5 6 7 8 9 # 1. General system messages: # Log all services \u0026gt;=info level, but exclude mail, authpriv, and cron logs *.info;mail.none;authpriv.none;cron.none /var/log/messages # 2. Debug logs: *.debug /var/log/debug # 3. Error-level logs: *.err /var/log/error.log Syntax rule key points: mail: service facility; info: log priority level; .: denotes matching log messages at greater than or equal to (\u0026gt;=) this priority; *.info: means all services\u0026rsquo; \u0026gt;= info level logs are aggregated and written to the target file. Real-Time Dynamic Log Monitoring Commands 1 2 3 4 5 # Follow the tail output of the system log tail /var/log/messages # Continuously tail the live log stream tail -f /var/log/messages 2. OpenStack Custom Service Log Troubleshooting Guidelines Troubleshooting principle: Each component usually has its own custom log-file system (under /var/log/\u0026lt;component\u0026gt;/). If a component misbehaves, prioritize its dedicated logs; only fall back to the system-wide log /var/log/messages when no custom log exists.\nCase Study: Locating Neutron Network Service Failures During deployment or operation, if virtual network creation fails or an OVS port does not come up, you can directly capture errors and warnings via regex matching:\n1 2 3 4 5 # 1. Regex-filter errors and warnings across all Neutron logs tail -f /var/log/neutron/*.log | grep -iE \u0026#39;(err|warn)\u0026#39; # 2. Deep-troubleshoot the neutron-server core API service log, printing 3 lines of context before and after each match: tail -f /var/log/neutron/server.log | grep -iE -A3 -B3 \u0026#39;(err|warn)\u0026#39; Parameter description:\n-i: ignore case; -E: enable extended regex matching (err|warn); -A 3 (After): print 3 lines after the match; -B 3 (Before): print 3 lines before the match. ","permalink":"https://LMDAWN1127.github.io/en/posts/openstack-core-architecture-deployment/","summary":"From core components (Nova/Glance/Neutron/Cinder, etc.) to one-click Packstack-based automated deployment of the Victoria release, plus Rsyslog log troubleshooting.","title":"OpenStack Cloud OS: Core Architecture and Automated Deployment"},{"content":"Architecture of Creating and Attaching Cinder Volumes Volume creation is handled by the Cinder node (the controller node), while attaching volumes is handled by Nova\nLocal LVM + iSCSI Volume Storage Configuration Specify in the answers.txt answer file that a Cinder volume should be created. Since the lab environment cannot provide dedicated storage hardware, the system uses local LVM as the Cinder backend The system creates a cinder-volumes volume group by default, 50 GB in size (specified manually by the user)\nThis volume group is created on the controller node\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 Call the LVM driver in /etc/cinder/cinder.conf so it recognizes the created volume group Treat this controller node as iSCSI storage (target) and share user-created volumes with the compute nodes over iSCSI When a user creates a 2 GB cloud disk, the system automatically invokes the LVM driver to create a 2 GB LV inside the volume group\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 When the user attaches this volume to the cloud server web01, Cinder sends a command to the storage side; the storage side then shares that LV with the compute host, so the compute host recognizes the disk, a new 2 GB disk appears, and libvirt invokes an XML file to add it to the cloud server\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; Storage-side configuration\nInstall the targetcli package and start the service 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 Target, service server, storage client 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 Connecting Cinder to NFS Storage Plan and configure the network according to the diagram above\n1. Configure NFS storage 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. Configure the Cinder node network (controller node) 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. Configure the compute node network 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. Configure 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 node\n1 2 [root@Controller ~]# cat /etc/hosts ####额外增加一条记录 192.168.8.20 storage Compute node\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-* In the OpenStack web UI, create a volume, choose nfs, and attach that volume to the cloud server\nIn the OpenStack web UI, create a volume, choose nfs, select an image as the source, and when creating the cloud server choose the newly created volume as the source\nOnce the cloud server is created, a volume is produced\nDelete the original cloud server, then create it again — this volume becomes usable\n","permalink":"https://LMDAWN1127.github.io/en/posts/openstack-cinder-block-storage/","summary":"A walkthrough of OpenStack Cinder block storage: how volume creation and attachment split between Cinder and Nova, the full local LVM + iSCSI path (VG/LV, turning the controller into an iSCSI target with targetcli, attaching via libvirt), plus NFS backend configuration and dashboard operations.","title":"OpenStack Cinder Block Storage Explained: Local LVM + iSCSI Volumes and NFS Backend Integration"},{"content":"Keystone Service OpenStack multi-domain support\n1 2 [root@Controller ~]# vim /etc/openstack-dashboard/local_settings OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True List all services in the system\n1 [root@Controller ~(keystone_admin)]# openstack service list Keystone address and API endpoint\n1 OS_AUTH_URL=http://192.168.100.10:5000/v3 All services know the Keystone address, because they need to ask Keystone to verify whether a user token is valid Keystone knows the addresses of all services, because every service registers itself with Keystone at creation time Endpoint addresses\n1 [root@Controller ~(keystone_admin)]# openstack endpoint list Each service has three endpoint addresses; these three URLs may be identical or different\npublic url — the URL used by external users\nadmin url — the URL used by administrators\ninternal url — the URL used for access between internal components\nCloud server provisioning flow\nThe user sends a request to Keystone with a username and password; once authentication succeeds, Keystone issues a token, and the user uses that token to send a create-cloud-server request to nova-api nova-api asks Keystone for help in validating whether that token is valid; if validation succeeds, the result is returned nova-api forwards the request to the nova-compute host The nova-compute host creates the cloud server according to the request and returns the result to nova-api nova-api returns the result to the user; the cloud server is created and its information is recorded in the database When I send a request to nova-api to create a server, I don\u0026rsquo;t necessarily know the nova-api address—but I do know the Keystone address. I query Keystone for the nova-api address, because Keystone stores the endpoint addresses of all services. Therefore I only need to know the Keystone address. The following shows how to look up the Keystone address and use it to resolve other service addresses.\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; When deploying OpenStack, a service is created\nEnter the database and create a database—for example, glance Create the database user glance and grant it ALL privileges (insert, delete, update, select) on all tables in the glance database Create the glance service and register the glance-api address (endpoint address) in the database When a user needs to access glance-api, they send a request to Keystone, which looks up the glance api endpoint address in its database\n1 2 #示例 如果将zlm用户加入admin角色，那他就拥有了admin角色权限 [root@Controller ~(keystone_admin)]# openstack role add --user zlm --project LMandMY_HOME admin When a user sends a request to nova-api, nova-api asks Keystone to verify the user\u0026rsquo;s identity and whether the token is still valid. It does not decide whether the user is authorized to create a cloud server—that depends on each service\u0026rsquo;s policy file\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 Service Controller node\nnova-api module\nnova-scheduler module\nnova-conductor module\nnova-novncproxy module\nCompute node\nnova-compute module\nnova-api\nHandles the externally exposed REST interface\nValidates and constrains the incoming parameters\nChecks and reserves quotas for the requested resources\nCreate, update, delete and query operations on resources\nThe entry point of the virtual machine lifecycle\nCan be deployed scaled out horizontally\nnova-conductor\nIt decouples nova-compute from direct database access: when a user requests a cloud server and creation succeeds, the nova-conductor module writes the server information into the database\ndatabase — stores information related to that component, such as cloud server info, image info and network info\nnova-scheduler\nWhen the OpenStack environment has multiple Compute nodes and a user creates a cloud server, the nova-scheduler module decides which compute node it runs on based on each compute host\u0026rsquo;s weight\nHAProxy provides load balancing: when a user wants to access nova-api, the request is handed to HAProxy, and depending on the load of each nova-api it decides which API node receives the request\nMQ — Message Queue (message queue)\nWhen a component consists of multiple modules, communication between modules goes through the message queue for congestion control and so on\nIf 1000 users send requests to nova-api\nnova-api sends the requests to nova-compute\nnova-api sends these 1000 requests to the message queue, which then delivers them to the nova-compute nodes in an orderly fashion according to each compute host\u0026rsquo;s message consumption and processing progress\nnova-compute\nThe module that actually runs virtual machines\nnova-novncproxy\nHow it works: the nova-compute host opens a dedicated VNC Server port for each running virtual machine\nThe nova-compute host must not expose that port to the outside; otherwise any host on the Internet could access the port and the virtual machine\nTo solve this, a nova-novncproxy service is started on the controller node and exposed externally. When a user opens that VM\u0026rsquo;s console, the request first goes to nova-novncproxy, which uses the supplied information (VM UUID, token, etc.) to find out which nova-compute runs the VM, and then forwards the request to that Compute host.\nHow opening a VM console via VNC works\nvncserver — port 5900\nvncviewer — vncviewer\nThe client does not need to install vncviewer; instead the functionality is integrated into browser HTML via the noVNC protocol\nGlance and Swift Service I. Deploying a cloud server:\nAfter the user issues a command to deploy a cloud server and it is validated, the nova-compute host downloads the image from the glance image server to local storage (the server itself also runs locally) — /var/lib/nova/instances/_base/\nAfter the cloud server boots, a differential disk is created; new writes go into that differential disk\nLab: store glance images in Swift object storage instead of on glance\u0026rsquo;s local disk\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 Note: the zlm user must be granted the swiftOperator role before it is allowed to operate on Swift\n","permalink":"https://LMDAWN1127.github.io/en/posts/openstack-keystone-glance-nova/","summary":"A walkthrough of OpenStack\u0026rsquo;s three core services: Keystone multi-domain support, service/endpoint registration and policy-file authorization; Nova\u0026rsquo;s modules (api/scheduler/conductor/compute/novncproxy), the message queue and the differential disk; and the Glance image service switched to a Swift object-storage backend.","title":"OpenStack Core Services Explained: Keystone Authentication, Nova Compute, and Glance/Swift Image Storage"},{"content":"I. Provisioning a Cloud Server via the Web Dashboard The admin user is the cloud super administrator\nadmin manages identities\nCreate a tenant (project) Create a user and associate it with a project Upload an image. Images uploaded by the admin can be set as public so all tenants can access them; images uploaded by ordinary users are private and can only be shared within the tenant. Create a flavor Create an external network Whichever node the L3 agent is deployed on becomes the network node\nLog in as a tenant\nLog in with the tenant identity Create a private network and subnet (VPC) Create a router and attach the external and private networks Create a security group and rules Create a key pair Provision the cloud server and associate a floating IP Testing (login, hostname, network) cloud-init initializes custom information such as hostname and password\nSuppose I create an image whose content is very large—about 20 GB of disk and requiring at least 4 GB of RAM to run smoothly. But the tenant doesn\u0026rsquo;t know this; when provisioning a server they pick a flavor with only 10 GB disk and 2 GB RAM, causing the provisioning to fail without understanding why. Therefore, when uploading the image I specify a minimum disk of 20 GB and minimum RAM of 4 GB, so that later, when choosing a flavor, any flavor smaller than that cannot be selected—only flavors meeting the requirement can be chosen.\nThe following is the key command in OpenStack Nova used to discover and map newly added compute nodes into their owning Cell database. Under the Cells v2 architecture, every compute node must complete this registration step; otherwise the scheduler cannot deploy instances on it and the API will not list that host.\n1 [root@Controller ~(keystone_admin)]# nova-manage cell_v2 discover_hosts --verbose A cloud server can reach the Internet through the router. The point of configuring a floating IP is to expose services to the outside—e.g., a web server can go out via SNAT, but remote users cannot come in to access that service. The following shows how to inspect the iptables rules OpenStack uses under the hood.\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. */ Lab requirement: create a cloud server that can access the Internet, point its yum repository to Alibaba Cloud, install the httpd package, start the service, and access the web page http://192.168.10.182 from an external host.\nA Compute host is essentially a KVM hypervisor. When OpenStack provisions a cloud server, the scheduler distributes it onto one of the Compute hosts.\nII. Provisioning a Cloud Server via the Command Line 1 [root@Controller ~(keystone_admin)]# cp keystonerc_admin keystonerc_zlm Requirement 1: Delete all resources using the 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 Delete the cloud server 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 Delete the volume 1 2 [root@Controller ~(keystone_zlm)]# openstack volume list [root@Controller ~(keystone_zlm)]# openstack volume delete data01 Delete the router 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 Delete the security group 1 2 [root@Controller ~(keystone_zlm)]# openstack security group list [root@Controller ~(keystone_zlm)]# openstack security group delete SG01 Delete the network 1 2 [root@Controller ~(keystone_zlm)]# openstack network list [root@Controller ~(keystone_zlm)]# openstack network delete VPC01 Switch to the admin identity to delete the public network, images, flavors, tenants, users, etc.\n1 [root@Controller ~(keystone_zlm)]# source keystonerc_admin Delete the public network 1 2 [root@Controller ~(keystone_admin)]# openstack network list [root@Controller ~(keystone_admin)]# openstack network delete public Delete the image 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 Delete the flavor 1 2 [root@Controller ~(keystone_admin)]# openstack flavor list [root@Controller ~(keystone_admin)]# openstack flavor delete m3.web Delete the tenant and user 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 Requirement 2: Create all resources via the CLI—provision a server, add a floating IP, access the server, and create networks\n1 [root@Controller ~(keystone_admin)]# source keystonerc_admin Create a project and user, and associate the user with the project 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 Upload an image 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 Create a flavor 1 [root@Controller ~(keystone_admin)]# openstack flavor create --ram 1024 --disk 10 --public m3.web Create the public network and subnet 1 [root@Controller ~(keystone_admin)]# openstack network create --external --share --provider-network-type flat --provider-physical-network extnet --project LMandMY_HOME public Create the subnet\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 Switch to the tenant identity\n1 [root@Controller ~(keystone_admin)]# source keystonerc_zlm Create the network and subnet 1 [root@Controller ~(keystone_zlm)]# openstack network create VPC01 Create the subnet\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 Create the router 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 Create the security group 1 [root@Controller ~(keystone_zlm)]# openstack security group create SG01 Allow the ICMP protocol\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol icmp --remote-ip 0.0.0.0/0 SG01 Allow the SSH service\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol tcp --dst-port 22 --remote-ip 0.0.0.0/0 SG01 Allow port 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 Create a floating IP 1 [root@Controller ~(keystone_zlm)]# openstack floating ip create public Create a key pair 1 [root@Controller ~(keystone_zlm)]# openstack key create key01 \u0026gt; key01.pem Create the cloud server 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 Associate the floating IP with the cloud server\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 Access the cloud server\n1 2 [root@Controller ~(keystone_zlm)]# chmod 400 key01.pem [root@Controller ~(keystone_zlm)]# ssh -i key01.pem cirros@192.168.10.182 If the external NIC cannot communicate\n1 [root@Controller ~(keystone_zlm)]# ifup br-ex ; ifup ens224 ","permalink":"https://LMDAWN1127.github.io/en/posts/openstack-provision-cloud-server/","summary":"From the web dashboard to the CLI, a complete walkthrough of provisioning OpenStack cloud servers: creating and deleting tenants/users/images/flavors/networks/routers/security groups/key pairs/floating IPs, plus iptables SNAT/DNAT and Nova cell_v2 discover_hosts troubleshooting.","title":"OpenStack Cloud Server Provisioning"},{"content":"01 - Organize the systemctl database Service files Option 1: Standard production daemon configuration 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 Option 2: With config file and file-handle limits 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 After the modification\n1 2 3 [root@master local]#systemctl daemon-reload [root@master local]#systemctl restart mysqld.service [root@master local]#systemctl enable mysqld.service 02 - Multi-version MySQL (5.6 / 5.7) install \u0026amp; upgrade-prep practice Step 1: Download the software archives 1 2 3 4 5 # MySQL 5.7.40 binary package [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 binary package [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.48-linux-glibc2.12-x86_64.tar.gz Step 2: Extract, install, and create symlinks 1 2 3 4 5 6 7 8 9 # install version 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 # Note: during multi-version coexistence debugging, do not set the global env var yet to avoid command-path conflicts # install version 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 # Note: do not configure the global env var for now Step 3: Initialize the database software MySQL 5.7 initialization (data directory 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 initialization (data directory 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 # missing Perl module [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 # check whether the output contains two OKs and confirm the exit code: [root@master local]#echo $? 0 Step 4: Write the multi-instance dedicated config files MySQL 5.7 (instance 3357) configuration:\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 (instance 3356) configuration:\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 Step 5: Run, start, and verify management Start/Stop the MySQL 5.7 service:\n1 2 3 4 5 6 7 8 9 10 11 # Method 1: start [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 # Method 2: start [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. Start/Stop the MySQL 5.6 service:\n1 2 3 4 5 6 7 8 9 10 11 # Method 1: start [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 # Method 2: start [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. Verify the status of the three services (all 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/en/posts/systemd-mysql-multiversion/","summary":"A hands-on guide to systemd Unit management and multi-version (5.6/5.7), multi-instance MySQL deployment: two production daemon configs, archive download, initialization, per-instance config files, and start/stop plus three-service status verification.","title":"systemd Service Management \u0026 Multi-version MySQL Deployment"},{"content":"01 - Database Service Installation and Deployment 1.1 Download the database server package Official download address (historical archived binary packages):\n1 2 3 # storage directory [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 Check the package information\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 Install the database server from a binary package Step 1: Prepare the OS environment 1. Confirm the network address 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. Configure hostname resolution 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. Disable the system firewall 1 [root@master local]#systemctl disable firewalld --now 4. Disable SELinux 1 2 3 4 5 [root@master local]#sed -i \u0026#39;s/^SELINUX=enforcing/SELINUX=disabled/\u0026#39; /etc/selinux/config # reboot to take effect [root@master local]#reboot [root@master local]#getenforce Disabled 5. Avoid conflicts during database installation 1 2 3 4 5 6 # check whether a bundled mariadb is present on the system [root@master local]#rpm -qa | grep mariadb mariadb-libs-5.5.68-1.el7.x86_64 # uninstall the bundled database packages [root@master local]#yum remove -y mariadb-libs 6. Install DB dependency packages (missing ones cause init errors) 1 [root@master local]#yum install -y libaio-devel Step 2: Install the database software 1. Extract the package and create a symlink (for easier version management) 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. Configure the system-wide environment variable 1 2 3 4 5 [root@master local]#vim /etc/profile # append at the end of the file export PATH=$PATH:/usr/local/mysql/bin # apply the configuration [root@master local]#source /etc/profile 3. Verify the installed version 1 2 [root@master local]#mysql -V mysql Ver 8.0.26 for Linux on x86_64 (MySQL Community Server - GPL) Step 3: Initialize the software MySQL has two core directories:\nProgram directory: /usr/local/mysql Data directory: /data/3306/data (stores the actual data) 1. Create a dedicated runtime user (no system login needed) 1 [root@master local]#useradd mysql -M -s /sbin/nologin 2. Create the data directory and set ownership 1 2 [root@master local]#mkdir /data/3306/data -p [root@master local]#chown mysql.mysql /data/3306/data 3. Initialize the data directory 1 2 3 4 5 # remove any interfering config files that may exist [root@master local]#rm -f /etc/my.cnf # run passwordless initialization (admin has no password afterward) [root@master local]#mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql Step 4: Write the database service config file Create and edit /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 Note: the basedir and datadir paths in the config must be adjusted to your actual environment.\nStep 5: Start the database service 1. Start with the official service management script 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! # being able to enter the database normally indicates a successful start [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. Start directly from the command line Start with the mysqld command: 1 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/data/3306/data --user=mysql --socket=/tmp/mysql.sock \u0026amp; Start with the mysqld_safe safe daemon: 1 /usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data \u0026amp; 3. Start with systemctl / service Method 1: write a dedicated Systemd unit file (/etc/systemd/system/mysqld.service) Method 2: directly enable and invoke the service script under /etc/init.d/: 1 2 3 4 systemctl enable mysqld service mysqld start # or systemctl start mysqld 1.3 Supplementary knowledge 1. Comparing the two initialization methods Method 1: insecure initialization (--initialize-insecure)\n1 mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql Characteristic: the DB admin has no password and can log in directly without one, convenient for testing or scripted initialization. Method 2: secure initialization (--initialize)\n1 mysqld --initialize --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql Characteristic: the DB admin gets a random temporary password printed in the log, used for the first login. Caution: the temporary password is only for the first login; after logging in you must reset a new password, and no other operations can run before changing it: 1 ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; 2. Standard troubleshooting flow for startup failures Step 1: Confirm the basic system environment Check whether SELinux is fully disabled; Check whether port 3306 is occupied and whether other conflicting DB service processes exist. Step 2: Investigate and analyze the error log Typical error: Starting MySQL. ERROR! The server quit without updating PID file (/data/3306/data/master.pid). Inspect the error log details: 1 2 3 cat /data/3306/data/master.err # example log error: # 2024-06-14T09:15:36.120048Z 1 [ERROR] [MY-012271] [InnoDB] The innodb_system data file \u0026#39;ibdata1\u0026#39; must be writable Step 3: How to troubleshoot when there is no error log Check the config file /etc/my.cnf: Confirm whether the config file path and filename are correct; Confirm whether the internal parameter syntax and path configuration are correct. 02 - Starting and stopping the database service There are several ways to start and stop the database service; the common ones are summarized below:\nNo. Start method Corresponding stop method Notes \u0026amp; suggestions 01 mysqld \u0026amp; kill / pkill / killall Not recommended; never use kill -9 02 mysqld_safe \u0026amp; kill / pkill / killall Direct kill not recommended 03 mysql.server start mysql.server stop Official native control script 04 service mysqld start service mysqld stop Recommended (classic service management) 05 systemctl start mysqld systemctl stop mysqld Recommended (modern Linux standard) 06 Client/admin command mysqladmin -uroot -p\u0026lt;password\u0026gt; shutdown Graceful shutdown via admin remotely or locally 07 Shut down within an SQL session Enter the terminal and run shutdown; Requires admin privileges 03 - Database service password management 1. Set a password when none exists Method 1: using the command line\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. Method 2: using an SQL statement\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. Change the password when one already exists Method 1: using the command line 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. Method 2: using an SQL statement 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. Forgot the password (password reset) Practical password reset procedure Step 1: Stop the database service\n1 mysql\u0026gt; shutdown; Or run in the system terminal:\n1 2 3 4 [root@master local]#service mysqld stop Shutting down MySQL.. SUCCESS! # or pkill mysqld Step 2: Start the database in safe mode (skip the grant tables to enable passwordless login)\n1 2 [root@master local]#/usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data --skip-grant-tables --skip-networking \u0026amp; [1] 2658 Note: --skip-networking disables network listening to prevent other network clients from intervening during the passwordless period.\nStep 3: Log in without a password and reset it\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; Run the following in the MySQL terminal in order:\n1 2 3 4 5 6 7 -- 1. Flush the privilege tables (sync in-memory and on-disk grant tables) -- Effect 1: write the in-memory grant table info to disk -- Effect 2: reload the on-disk grant table info into memory FLUSH PRIVILEGES; -- 2. Reset the password ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Note: if you run ALTER USER directly without FLUSH PRIVILEGES;, the system throws an error:\n1 ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement Step 4: Restart the database service normally\n1 2 3 4 5 6 7 # terminate the temporary safe-mode process [root@master local]#pkill mysqld # start the database service normally [root@master local]#/etc/init.d/mysqld start Starting MySQL.. SUCCESS! # or service mysqld start Step 5: Verify login with the new password\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/en/posts/mysql-binary-deploy/","summary":"A complete MySQL 8.0 operations guide: installing from the official binary package, OS prep, initialization, config file, multiple start/stop methods, plus password set/change and forgot-password reset.","title":"MySQL in Practice: Binary Installation, Start/Stop, and Password Management"},{"content":"01. SonarQube Introduction 1 2 3 1. SonarQube is developed in Java and requires Open JDK 8 2. SonarQube depends on a MySQL database, at least version 5.6 3. A small SonarQube instance needs at least 4G of memory; a large instance needs 16G 02. Deployment 1. Install the JDK runtime 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. Install the database 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. Download the official MySQL Yum repository config file [root@sonarqube ~]#wget dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm # 2. Install the MySQL repository [root@sonarqube ~]#rpm -ivh mysql-community-release-el6-5.noarch.rpm Preparing... ################################# [100%] Updating / installing... 1:mysql-community-release-el6-5 ################################# [100%] # 3. Install the MySQL server [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. Start the MySQL service [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. Set the password [root@sonarqube ~]# mysqladmin -uroot password 000000 # 6. Create the sonarqube database [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. Install 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. Upload the SonarQube package [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. Extract into the current directory [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. Install plugins: Remove the default plugins: [root@sonarqube ~]# rm -rf /usr/local/sonarqube/extensions/plugins/* Upload the 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. Create a normal user [root@sonarqube ~]# useradd sonarqube # 5. Change ownership of the code to user/group 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. Configure the database connection [root@sonarqube ~]#vim /usr/local/sonarqube/conf/sonar.properties ... sonar.jdbc.username=root # database username sonar.jdbc.password=000000 # database password #----- 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 # enable local connection ... 4. Run SonarQube 1 2 SonarQube cannot be started as root; it must run as a normal user [root@sonarqube ~]# su - sonarqube -c \u0026#34;/usr/local/sonarqube/bin/linux-x86-64/sonar.sh start\u0026#34; 5. Configure the remote access token After startup the port is 9000; open http://11.0.1.202:9000/about in a browser, log in as admin / admin, and generate a token — it is shown only once, so record it.\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. Client deployment Client 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. Upload the client [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. Extract and create a symlink [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. Set the global PATH variable [root@jenkins ~]# export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 4. Persist the variable in /etc/profile [root@jenkins ~]# vim /etc/profile .. export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 5. Test the client by scanning code to the server for analysis Push the blog code under /var/lib/jenkins/workspace/test_job/ to SonarQube for testing [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 Execution succeeded:\nCheck the server: upload succeeded\n","permalink":"https://LMDAWN1127.github.io/en/posts/sonarqube-practice/","summary":"A complete hands-on SonarQube self-hosting guide: JDK 8 and MySQL 5.6 setup, extracting SonarQube with plugins, running as a non-root user, web token configuration, and scanning code with the sonar-scanner client.","title":"SonarQube in Practice: Self-hosted Deployment, Token Setup, and Code Quality Scanning"},{"content":"01. Jenkins Introduction Official site: jenkins.io\nJenkins is an open-source project — a Java-based continuous integration tool that monitors repetitive tasks and provides an open, easy-to-use platform to make software CI possible.\n02. Jenkins Deployment 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. Environment preparation 1. OS: CentOS 7.9 2. Memory: 2G for lab, at least 6G for production 3. Disable the firewall and turn off SELinux 4、JDK17 II. Install the JDK Upload the RPM package [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%] Check whether the installation succeeded: [root@jenkins ~]# rpm -qa|grep jdk jdk-17-17.0.11-7.x86_64 II. Install Jenkins Upload the Jenkins package [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. Change the Jenkins run user to root 1) Change the run user in the config file [root@jenkins ~]# vim /etc/sysconfig/jenkins ... JENKINS_USER=\u0026#34;root\u0026#34; ... 2) Change the run user in the service config [root@jenkins ~]# vim /usr/lib/systemd/system/jenkins.service ... User=root Group=root ... [root@jenkins ~]# systemctl daemon-reload 3) Install font configuration [root@jenkins ~]# yum -y install fontconfig IV. Start Jenkins and enable it on boot [root@jenkins ~]# systemctl start jenkins [root@jenkins ~]# systemctl enable jenkins 03. Log in to Jenkins 04. Change the password After changing the password you are logged out; log back in as admin\n05. Plugin installation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1. Upload plugins to the Jenkins home directory [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. Extract the plugins into the plugins directory [root@jenkins jenkins]#tar xf /root/jenkins_plu.tar.gz -C plugins/ 3. Restart Jenkins to apply [root@jenkins ~]# systemctl restart jenkins 06. Create a project Create a freestyle project and run pwd to see the default working directory Jenkins uses\nNote: after Jenkins creates a project, it automatically creates a workspace directory under its home /var/lib/jenkins/, and inside it a directory named after the project.\nSubsequently, the code Jenkins pulls from GitLab is stored under this project directory.\n1 2 [root@jenkins ~]#ls /var/lib/jenkins/workspace/ test_job Connect GitLab with Jenkins\nBefore it is connected, a \u0026ldquo;cannot connect\u0026rdquo; error is shown:\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 # configure keyless (SSH) access [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 # install git on Jenkins [root@jenkins ~]#yum install -y git Add the key generated by Jenkins into GitLab:\nRun git clone once on Jenkins to complete the initial yes prompt\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. Then the connection works\nTrigger another build to test the pull\nPull succeeded\nConfigure the webhook\nConfigure a webhook so Jenkins automatically pulls GitLab code and builds on each push\nSave both sides, then test\nAfter modifying the project, push again to test\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. Automatic build succeeded\n","permalink":"https://LMDAWN1127.github.io/en/posts/jenkins-practice/","summary":"A complete hands-on Jenkins self-hosting guide: RPM install of JDK and Jenkins, running as root, offline plugin install, and wiring GitLab SSH keyless access with webhook-triggered CI builds.","title":"Jenkins in Practice: Self-hosted Deployment, Plugin Setup, and GitLab-Driven CI"},{"content":"01. GitLab Websites Official site: https://about.gitlab.com/\nChina mirror: https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/\n02. GitLab Deployment 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. Environment preparation 1. OS: CentOS 7.9 2. Memory: 4G for lab, at least 8G for production 3. Disable the firewall and turn off SELinux II. Download the GitLab RPM package [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. Install GitLab # During installation, dependencies are required [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 # install dependencies [root@gitlab ~]# yum -y install policycoreutils-python # continue installing GitLab after dependencies are ready [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. Configure the access domain or IP before starting [root@gitlab ~]# vim /etc/gitlab/gitlab.rb ... external_url \u0026#39;http://11.0.1.200\u0026#39; ... After the configuration, run the command: [root@gitlab ~]# gitlab-ctl reconfigure 03. GitLab Components 1 2 3 4 5 6 7 nginx: static web server gitlab-workhorse: lightweight reverse proxy logrotate: log file management tool postgresql: database redis: cache database sidekiq: runs queued background jobs (asynchronously). (Ruby) unicorn: An HTTP server for Rack applications; the GitLab Rails app is hosted on it. (Ruby Web Server, written mainly in Ruby) 04. Common GitLab Commands 1 2 3 4 5 gitlab-ctl start # start GitLab (services start by default after gitlab-ctl reconfigure) gitlab-ctl status # check the running status of all GitLab services gitlab-ctl stop # stop GitLab services gitlab-ctl stop nginx # stop a single service gitlab-ctl tail # view logs of all services 05. GitLab Web UI Configuration Log in to GitLab at: 11.0.1.200\n1. Login password After gitlab-ctl reconfigure finishes, it shows where the password is stored:\n2. Switch the UI language to Chinese 3. Change the default password 4. Allow local network requests for webhooks 06. GitLab Hands-on 1. Create a group 2. Create a project under the group At this point the first code repository is created, but it is empty. Next, upload code.\n3. Commit code into the project After the project is created, upload the blog code into this project.\nThere are two ways to connect the server with the code repository:\nFirst: connect using username and password.\nSecond: connect via SSH keyless authentication.\nWe use the keyless (SSH) method: convenient and more secure.\nGenerate a key pair (here we use the GitLab server directly). 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]-----+ View the key pair: [root@gitlab ~]# ll .ssh/ total 8 -rw------- 1 root root 1679 May 14 15:02 id_rsa # private key -rw-r--r-- 1 root root 393 May 14 15:02 id_rsa.pub # public key [root@gitlab ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJGpCKL64eidZ3I/nAVNCNJwD8yeDqelZP0GUTwlcp6IdKaZ7wUOIRDCtGn5r+YnOCKNwMhUNzb3VCOSVLDVIE0x53dPrCMK4OKLmZ6OtTYFBveGOZWWwdYyGQITx2eP7zOSBxeW3AnEWcya/UZdiZrYXA/d29DWjMjCt5QAxOit0EWSir1Z4i2MkTPuljOmjk6A55XAoem0WJRdg/d8KKIiCdcGrj4adYq2kNJ8oYH/9QcnpASUW/ANhzrALnLIX2WaH/p0T3gisLvk2KDUe3rVKD3XaKDlyLgWIViQFm3Me6L2e4taRFiu3VdsbfFfzjDevfDtLaqKLOqPtaczlF root@gitlab Paste the generated public key into the SSH key management page of the GitLab UI. This completes the SSH keyless setup between the server and the GitLab UI. Next, push the code into the blog project.\nThere are two ways to push code:\nClone the empty repo locally, copy the code into it, then commit to the remote GitLab repo. Go into the project and find the project 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. Copy the link and run the command. [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. Verify the blog repo was downloaded locally. [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. Enter the blog directory. [root@gitlab ~]#cd blog/ 4. Extract the code: [root@gitlab blog]#tar xf /root/blog_backup_20261001-1435.tar.gz -C . 5. Commit the code to the local repository. [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. Push the code to the blog repo on the remote GitLab. Since we cloned the GitLab repo directly, the local directory already has the remote blog project address. Check the remote repository: [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) Push the local master branch to the remote 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. View the blog project; the code upload succeeded.\nPush a local Git repo to the remote 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. Set up a local repository. [root@gitlab ~]#mkdir dawn [root@gitlab ~]#cd dawn 2. Initialize the repository. [root@gitlab dawn]#git init 3. Upload the code. [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. Set the GitLab blog repo as the local remote. [root@gitlab dawn]#git remote add origin git@11.0.1.200:dawn/blog.git 5. Push local content to the remote (the remote already has content, so a direct push errors; keep the remote content by pulling/merging first, then pushing). [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. Create a normal user \u0026ldquo;dev\u0026rdquo;. After creation, change the dev password — do not log out; click Edit on the user page.\nAfter that, log in to GitLab as dev (you must change the password on first login).\nAdd the dev user to the dawn group. Generate a key pair on the dev server. 1 [root@lb01 ~]#ssh-keygen Copy the generated public key into the SSH-key page under the dev account. Clone the code to the dev host 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. Modify and upload code to the remote repository. 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 Set the user identity on 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; Commit to the remote repository. [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; # pushing to the master branch is not allowed Push to another branch: 1. Create the dev branch. [root@lb01 blog]#git branch dev [root@lb01 blog]#git branch dev * master 2. Push the dev branch to the remote. [root@lb01 blog]#git push -u origin dev Merge Request (MR)\nInitiate a merge request from the dev account. In the root account, approve the merge and delete the source dev branch. Download the code on the web server. 1 2 [root@web01 ~]#git clone http://11.0.1.200/dawn/blog.git Enter the username and password. ","permalink":"https://LMDAWN1127.github.io/en/posts/gitlab-practice/","summary":"A complete hands-on guide to self-hosting GitLab: RPM install, components, common commands, web UI setup, SSH keyless access, and multi-user collaboration.","title":"GitLab in Practice: Self-hosted Deployment, SSH Keyless Access, and Repository Management"},{"content":"01. Install Git and Initialize a Repository 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 Commands 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. Check repository status (always keep the working tree clean) [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. Create a plain file [root@gitlab dawn]#touch a.txt 3. Stage the working tree content [root@gitlab dawn]#git add a.txt # git add . stages everything in the working tree [root@gitlab dawn]#git add . 4. Remove content from the staging area [root@gitlab dawn]#git rm --cached a.txt rm \u0026#39;a.txt\u0026#39; 5. Stage it and commit to the local repository [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 # # commit to the local repository with git commit -m plus a message [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 Check status: [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean Create a file touch a.txt Stage git add a.txt Commit to local repo git commit -m \u0026#34;xxx\u0026#34; 2. Deleting with 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 Note: if you regret staging a file, remove it from the staging area [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. Restore the working tree content [root@gitlab dawn]#git reset HEAD b.txt [root@gitlab dawn]#git checkout -- b.txt 4. Diffing with git git diff compares the working tree against the staging area [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 compares the staging area against the local repository [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. Renaming a file [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. Roll back to a specific snapshot Show all snapshots: [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 Roll back with 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. View all historical actions: [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 branches 1. List branches (* marks the current branch) [root@gitlab dawn]#git branch * master 2. Create a branch [root@gitlab dawn]#git branch dev [root@gitlab dawn]#git branch dev * master 3. Switch to the dev branch [root@gitlab dawn]#git checkout dev Switched to branch \u0026#39;dev\u0026#39; [root@gitlab dawn]#git branch * dev master 4. Create a file on the branch and commit it to the local repository [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. Merging branches First switch back to the master branch [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master Merge the dev branch into 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. Delete the branch right after finishing the feature (it falls behind the remote branch) [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 2694c7f). [root@gitlab dawn]#git branch * master 7. Merge conflicts ---------------------------------- Editing a file does not necessarily block the merge Edit the file on the master branch and add bbbbb [root@gitlab dawn]#vim b.txt aaaaa bbbbb Create and switch to the dev branch with a single command [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 Edit the content of b.txt [root@gitlab dawn]#vim b.txt aaaaa bbccc Commit to the local repository [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;modifle bbbccc\u0026#34; Switch back to master and merge the dev branch [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 ------------------------------------------------------ A case that triggers a merge conflict Create the dev branch [root@gitlab dawn]#git branch dev On the master branch, edit line 3 of b.txt [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(+) Switch to the dev branch and edit line 3 of b.txt [root@gitlab dawn]#cat b.txt aaaaa iiiii ccccc Commit to the local repository [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(+) Switch back to master and merge the branch [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. Inspect the file to see the conflict markers [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 Resolve the conflict manually, keeping one side or both [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 Delete the dev branch after merging [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 925c06c). ","permalink":"https://LMDAWN1127.github.io/en/posts/git-practice-notes/","summary":"A hands-on Git walkthrough covering installation, everyday commands, branch workflows, and conflict resolution.","title":"Git in Practice: Installation, Essential Commands, and Branch Management"},{"content":"1. Get the Number of Logged-in Users 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. Define It as a Zabbix Monitoring Item 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 can be used to check the item\u0026#39;s syntax [root@web02 ~]#zabbix_agentd -p|grep login_number login_number [t|2] # restart the agent [root@web02 ~]# systemctl restart zabbix-agent.service 3. Collect and Monitor from the Zabbix Server Side From the server, use the command line to test whether the custom value can be fetched through the monitoring item.\n1 2 3 4 # install the zabbix-get command [root@zabbix ~]# yum -y install zabbix-get [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k login_number 2 Add the monitoring item on the web page\nView the monitoring item\n4. Monitor Whether TCP Port 22 Is Alive Select the key value that matches the information you want to monitor.\nCheck whether the TCP port is in the LISTEN state. Returns 0 - not listening; 1 - listening.\nStop the sshd service on web02\n1 [root@web02 ~]# systemctl stop sshd 5. Configure a Custom Value Mapping for Display Apply the value mapping\n6. Custom Monitoring of Server Memory Percentage Step 1: Define a custom item to get the free-memory percentage\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] Step 2: Test fetching the key value from the server and add the monitoring item\n1 2 [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k mem_free 79.9775 7. Set a Trigger for Available Memory Trigger an alert when the available-memory percentage drops below 20%.\nTest: turn off the swap partition\n1 [root@web02 ~]# swapoff -a Use dd to stress-test and consume memory\n1 [root@web02 ~]# dd if=/dev/zero of=/dev/null bs=1200M count=2048 8. Configure a Multi-condition Trigger Trigger an alert only when both conditions are met simultaneously.\nAdd the swap-free percentage\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; Modify the trigger configuration\nThen run the dd stress test again to check the result.\n","permalink":"https://LMDAWN1127.github.io/en/posts/zabbix-custom-monitor/","summary":"A hands-on Zabbix custom monitoring guide — collect custom metrics such as login user count and free-memory percentage with UserParameter, verify them via zabbix_get, then add items, value mappings, and triggers (including a multi-condition trigger) on the web UI for custom alerting.","title":"Custom Monitoring in Zabbix"},{"content":"For high-concurrency scenarios in enterprise Zabbix deployments.\n1. Prepare a Standalone Database Server (11.0.1.52) and Install 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. Export the Zabbix Database from the Zabbix Server and Import It into the New Database 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. Configure the Web UI\u0026rsquo;s Database Connection 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. Restart the Services 1 [root@zabbix ~]# systemctl restart zabbix-server httpd ","permalink":"https://LMDAWN1127.github.io/en/posts/zabbix-split-database/","summary":"For high-concurrency enterprise Zabbix scenarios — move the database off the Zabbix server onto a dedicated MariaDB server (11.0.1.52): create the database and grant privileges, export and import the data, update the Web UI and Server database connection settings, then restart the services.","title":"Splitting the Zabbix Database"},{"content":"1. Install the zabbix-agent Client — Prepare a Server (11.0.1.8) 1 2 3 4 5 6 7 8 Configure the repository: [root@web02 ~]# rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm Sync the yum repositories from the server to the client: [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 If the installation reports a package conflict: [root@web02 ~]# yum -y install centos-release-scl 2. Configure the zabbix-agent with the Server Info and Start It 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. Monitor web02 from Zabbix 4. web02 Monitoring Setup Complete ","permalink":"https://LMDAWN1127.github.io/en/posts/zabbix-monitor-server/","summary":"How to deploy the Zabbix agent on a target server (web02, 11.0.1.8), point it at the Zabbix server, and start monitoring it from the Zabbix web UI.","title":"Monitoring Server Information with Zabbix"},{"content":"1. Configure the Repository 1 rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 2. Install zabbix-server and zabbix-agent 1 yum -y install zabbix-server-mysql zabbix-agent 3. Install the SCL Repository (to Resolve Version Conflicts) 1 [root@zabbix ~]# yum -y install centos-release-scl 4. Enable the Frontend Repository 1 2 3 4 5 [root@zabbix ~]# vim /etc/yum.repos.d/zabbix.repo [zabbix-frontend] ... enabled=1 ... 5. Replace the Broken mirrorlist in CentOS-SCLo-scl.repo and CentOS-SCLo-scl-rh.repo with Aliyun baseurls 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. Install the PHP/Apache Frontend 1 [root@zabbix ~]# yum -y install zabbix-web-mysql-scl zabbix-apache-conf-scl 7. Install the MariaDB Database 1 2 3 [root@zabbix ~]# yum -y install mariadb-server Start the database: [root@zabbix ~]#systemctl enable mariadb --now 8. Create the Zabbix Database 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. Initialize the Database (Import the Schema Required Before Zabbix Runs) 1 2 3 4 5 [root@zabbix ~]# zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix # You will be prompted for the zabbix password [root@zabbix ~]# mysql -uroot MariaDB [(none)]\u0026gt; set global log_bin_trust_function_creators = 0; quit 10. Configure Zabbix\u0026rsquo;s Database Connection 1 2 3 4 [root@zabbix ~]# egrep ^DB /etc/zabbix/zabbix_server.conf DBName=zabbix DBUser=zabbix DBPassword=zabbix # Uncomment and set the password 11. Set the Timezone 1 2 [root@zabbix ~]# tail -1 /etc/opt/rh/rh-php72/php-fpm.d/zabbix.conf php_value[date.timezone] = Asia/Shanghai 12. Start Services (server, agent, httpd, php) and Enable on Boot 1 [root@zabbix ~]# systemctl enable zabbix-server zabbix-agent httpd rh-php72-php-fpm --now 13. Access the Zabbix Web UI at 11.0.1.71/zabbix 14. Zabbix Installation Complete 15. Switch to Chinese and Reconfigure the Font Locate a Windows font and copy it into Zabbix\u0026rsquo;s font directory.\nWhere Windows fonts are stored:\nUpload one of the fonts, then rename it:\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 # the uploaded font [root@zabbix fonts]#rm -f graphfont.ttf # remove the original font [root@zabbix fonts]#ln -s /usr/share/zabbix/assets/fonts/simhei.ttf graphfont.ttf # link to the uploaded font [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/en/posts/zabbix-install/","summary":"Step-by-step guide to installing Zabbix 5.0 on CentOS 7 — configuring repositories, installing the server/agent/frontend, setting up MariaDB, importing the schema, and switching the web UI to Chinese with a custom font.","title":"Installing and Deploying Zabbix"},{"content":"Planning is as follows:\nMaster node: Hostname: master IP address: 192.168.8.30\nWorker01 node: Hostname: node1 IP address: 192.168.8.31\nWorker02 node: Hostname: node2 IP address: 192.168.8.32\nComplete the following steps on all three nodes, and make sure the clocks of all three nodes are synchronized.\n1. Disable Firewall and 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. Disable Swap 1 2 [root@master ~]# sed -i \u0026#39;/swap/s/^/#/\u0026#39; /etc/fstab [root@master ~]# swapoff -a 3. Configure System Parameters and Load Kernel Modules 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. Install the Container Runtime (Containerd) Kubernetes v1.30 recommends using Containerd as the container runtime:\n1 [root@master ~]# yum -y remove runc Remove the OS-bundled yum repository files:\n1 [root@master ~]# rm -rf /etc/yum.repos.d/Cent*.repo Configure the local yum repository:\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 Configure 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 Install containerd.io:\n1 2 3 4 5 6 7 8 9 10 [root@master ~]# yum install -y containerd.io # Generate and edit the configuration file [root@master ~]# mkdir -p /etc/containerd [root@master ~]# containerd config default \u0026gt; /etc/containerd/config.toml # Edit the config to use the systemd cgroup driver #sed -i \u0026#39;s/SystemdCgroup = false/SystemdCgroup = true/g\u0026#39; /etc/containerd/config.toml # Configure image acceleration (optional) #sed -i \u0026#39;s|registry.k8s.io|registry.aliyuncs.com/google_containers|g\u0026#39; /etc/containerd/config.toml # Start and enable on boot #systemctl enable --now containerd 5. Install and Configure 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 Install the specified version of the Kubernetes components:\n1 2 3 [root@master ~]# yum install -y kubelet-1.30.0 kubeadm-1.30.0 kubectl-1.30.0 --disableexcludes=kubernetes # Start kubelet [root@master ~]# systemctl enable --now kubelet 6. Initialize Kubernetes Note: Once all nodes have completed the configuration above, run the following steps only on the master node.\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 # Configure 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. Join Worker Nodes to the Kubernetes Cluster Run the following commands on the worker nodes. Note: the token and CA certificate hash differ per node — follow the prompt from your own system. The certificate may also expire; if it has, regenerate the join command with 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. Install the Calico Network Plugin on the Master Node 1 2 # Install the Calico network plugin [root@master ~]# kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml Note: For well-known reasons, you may encounter issues downloading the Calico image.\nI have revised that manifest file — download it to the master node and apply it:\nThe file is on Baidu Netdisk. Download link: https://pan.baidu.com/s/1BPCFxv0kE0KWb04Qsgy71g (extraction code: s44k)\n1 [root@master ~]# kubectl apply -f /root/calico-ucloud.yaml After installation, run:\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 If you see the output above, Kubernetes has been installed successfully.\n9. Additional Content Run the following commands on every node:\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 Note:\nkubectl shell completion: The first two lines write kubectl\u0026rsquo;s completion script into /etc/bash_completion.d/. This only works if the bash-completion package is installed on the system first (e.g. yum install -y bash-completion or apt install -y bash-completion); otherwise that directory does not exist and completion will not work. kubectl itself also requires these two commands to actually enable completion (Kubernetes needs this extra configuration). docker alias: After Kubernetes switches to the containerd runtime, container management uses crictl instead of docker. Aliasing docker to crictl here is for users accustomed to the docker command, so they can keep their familiar workflow. If you run # docker images and encounter the following error:\nKubernetes shows the following warning — here is how to resolve it.\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.\nThis warning indicates that the container runtime client you are using (such as crictl or kubelet) is using the default endpoint configuration, which is now deprecated and must be set explicitly. The detailed steps to fix it are as follows:\n1 2 3 4 5 6 7 cat \u0026gt; /etc/crictl.yaml \u0026lt;\u0026lt; EOF runtime-endpoint: unix:///run/containerd/containerd.sock # Choose according to your container runtime image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false pull-image-on-create: false EOF Notes:\nIf you use Containerd, the endpoint is unix:///run/containerd/containerd.sock.\nIf you use CRI-O, the endpoint is unix:///run/crio/crio.sock.\nRemark: Optional.\nIf you installed Kubernetes manually, it uses iptables for data forwarding by default. For large-scale scenarios, it is recommended to switch to IPVS mode. The steps to switch to IPVS mode after installation are as follows:\n1. Install the ipvsadm package on all nodes 2. Load the modules on all nodes 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. Edit the kube-proxy ConfigMap 1 kubectl edit cm kube-proxy -n kube-system In the editor that opens, find and modify the following section:\n1 2 3 4 5 6 7 apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: \u0026#34;ipvs\u0026#34; # change from \u0026#34;iptables\u0026#34; to \u0026#34;ipvs\u0026#34; ipvs: scheduler: \u0026#34;rr\u0026#34; # load balancing algorithm: rr|wrr|lc 4. Force a restart of the kube-proxy Pods Delete all kube-proxy Pods to trigger recreation:\n1 #kubectl delete pods -n kube-system -l k8s-app=kube-proxy 5. Verify the new Pods are running normally 1 2 #kubectl get pods -n kube-system -l k8s-app=kube-proxy # All Pods should be in Running state 6. Verify the IPVS configuration is effective 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/en/posts/k8s-v1.30-manual-deploy/","summary":"A step-by-step guide to manually deploying a Kubernetes v1.30 cluster with kubeadm — covering firewall/SELinux, swap, kernel modules, Containerd, kubeadm init, worker join, Calico, and an IPVS add-on.","title":"Manually Deploying a Kubernetes v1.30 Cluster (via kubeadm)"},{"content":" This article systematically introduces KVM virtualization technology, covering its development history, deployment methods, virtual machine management, network mode configuration, and automated VM installation via command line. All commands have been verified on CentOS/RHEL environments.\nI. History of KVM KVM (Kernel-based Virtual Machine) is the dominant virtualization technology in today\u0026rsquo;s cloud computing landscape. Understanding its development history helps explain why the industry ultimately chose KVM.\nVirtualization technologies can be broadly divided into two categories: proprietary virtualization (e.g., VMware, Microsoft Hyper-V) and open-source virtualization (e.g., Xen, KVM).\nThe early representative of open-source virtualization was Xen. AWS and Alibaba Cloud both adopted Xen in their early days; Citrix\u0026rsquo;s XenServer and the desktop cloud product FusionAccess (corresponding to Citrix XenDesktop) were also based on Xen. Huawei\u0026rsquo;s FusionCompute used Xen prior to version 6.3.\nKey milestones are as follows:\n2008: Red Hat acquired KVM and officially released it in RHEL 5.4 (RHEL 5.0 shipped with Xen). 2010: The OpenStack project was born. Red Hat, together with IBM, Dell, VMware, Cisco, HP, and other vendors, embraced the KVM open-source community and built the ecosystem under the OpenStack Foundation. During the same period, Citrix promoted the CloudStack community. 2014: RHEL 5.11 was released (the end of life for the RHEL 5.x series, which provided 7 years of technical support and free upgrades to KVM). Around 2017: AWS launched KVM-based Nitro platform instances. Alibaba Cloud and numerous global vendors fully embraced KVM. Today, the underlying virtualization technology of major cloud providers is essentially KVM. Huawei\u0026rsquo;s FusionCompute 6.3 also switched from Xen to KVM. In 2019, IBM acquired Red Hat for approximately $34 billion, bringing projects such as Ansible, GlusterFS, and Ceph under its umbrella, further validating the value of this technology path.\nII. What is KVM KVM stands for Kernel-based Virtual Machine. Its core characteristics are:\nNo kernel upgrade required: It directly reuses the system\u0026rsquo;s own kernel, with each virtual machine running as a process. Lightweight virtualization platform: More compact and efficient compared to traditional solutions. Unified management: Both Xen and KVM virtualization platforms provide unified management platforms and command-line tools. III. Deploying KVM 1. Install Required KVM Packages Prerequisite: Enable virtualization support (VT-x) in the physical machine\u0026rsquo;s BIOS (enabled by default on most computers). If installing KVM nested inside VMware, you also need to enable VT-x support in VMware settings.\nRecommended resource allocation: 4 CPU cores, 4 GB RAM.\n1 2 [root@cloud ~]# yum -y groupinstall \u0026#34;Virtualization*\u0026#34; [root@cloud ~]# systemctl restart libvirtd.service 2. Graphical VM Installation This lab uses the graphical tool virt-manager to create and install virtual machines.\nNote: The graphical wizard steps are not shown in screenshots in this article. The focus is on command-line operations; the graphical interface can be completed by following the prompts. Additionally, \u0026ldquo;host machine\u0026rdquo; refers to the machine running KVM virtual machines. This lab performs nested virtualization in VMware, so the host machine here is a VMware virtual machine, not a physical machine.\nFirst, prepare the system installation image: create an /isos directory and copy the CentOS 8.4 ISO file into it.\n1 [root@cloud ~]# mkdir /isos Then launch the graphical management interface:\n1 [root@cloud ~]# virt-manager In the interface, click to create a new virtual machine, select the ISO placed in /isos as the installation source, and follow the wizard to configure CPU, memory, and disk to complete the installation. The disk file of the installed virtual machine is saved by default in the /var/lib/libvirt/images/ directory, named like server1.qcow2:\n1 2 3 [root@cloud ~]# cd /var/lib/libvirt/images/ [root@cloud images]# ls server1.qcow2 3. Creating a Disk During the graphical installation in the previous step, virt-manager automatically creates the disk. Here we introduce the manual method, which will be used later for creating cloud images and adding disks to virtual machines. Use qemu-img to create a qcow2 format disk:\n1 [root@cloud ~]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server1.qcow2 10g Tip: If you want to create a cloud image this way, a smaller capacity means faster creation. You can research how to make the image as small as possible. Typically, the system disk does not require high performance; the real performance bottleneck is on the data disk.\n4. Creating Bridge Mode (Virtual Switch with Uplink) Enter the network configuration directory and copy a bridge configuration based on the existing network interface:\n1 2 [root@cloud ~]# cd /etc/sysconfig/network-scripts/ [root@cloud network-scripts]# cp ifcfg-ens33 ifcfg-br0 Edit the bridge interface 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 Edit the physical interface ifcfg-ens33 to bridge it to br0:\n1 [root@cloud network-scripts]# cat ifcfg-ens33 1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 Reload the network configuration and restart the connections:\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. Creating a Cloud Image Requirement: Create a custom cloud machine image.\nAfter deploying the KVM virtual machine, enter the VM and install some necessary software:\nConfigure a local yum repository Install necessary packages: 1 # yum -y install net-tools vim-enhanced bash-completion cloud-init Edit the /etc/cloud/cloud.cfg configuration file and adjust parameters as needed Delete /etc/ssh/ssh_host* Run init 0 to shut down Finally, compress the image:\n1 [root@cloud images]# qemu-img convert -c -O qcow2 /var/lib/libvirt/images/server1.qcow2 /isos/server1.qcow2 IV. Managing KVM Enter the virsh interactive interface:\n1 [root@cloud ~]# virsh Common virtual machine management commands are as follows.\nList all virtual machines:\n1 2 3 4 [root@cloud ~]# virsh list --all Id Name State ------------------------- - server1 shut off Start / Shut down / Force off / Reboot a virtual machine:\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. Accessing the VM Console Interface To enter a KVM virtual machine, add a virtual console to the VM, enabling console access from the host machine.\nEdit the grub configuration inside the virtual machine:\n1 [root@VM1 ~]# vim /etc/default/grub Append console=ttyS0 to the end of GRUB_CMDLINE_LINUX:\n1 GRUB_CMDLINE_LINUX=\u0026#34;crashkernel=auto resume=UUID=94e88e69-fad6-4d1f-99cf-0ba83cc793b5 rhgb quiet console=ttyS0\u0026#34; Regenerate the grub configuration and reboot:\n1 2 [root@VM1 ~]# grub2-mkconfig -o /boot/grub2/grub.cfg [root@VM1 ~]# reboot Connect to the console from the host machine:\n1 [root@cloud ~]# virsh console server1 The shortcut to exit the console is Ctrl + ].\n7. Adding a Disk to a VM via Configuration (1) Create a disk file:\n1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/disk2.qcow2 5g (2) Add the disk via the configuration file:\n1 [root@cloud ~]# vim /etc/libvirt/qemu/server1.xml Add the following disk section to the configuration:\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; Restart the service and start the virtual machine:\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server1 [root@cloud ~]# virsh console server1 Verify the disk inside the KVM virtual machine:\n1 2 3 [root@localhost ~]# fdisk -l /dev/vda /dev/vdb Comparison: Under Xen virtualization, disk device names are /dev/xvda, /dev/xvdb.\n8. Quickly Creating a KVM Virtual Machine Quickly create a new VM by copying an existing VM\u0026rsquo;s XML configuration:\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 Modify key parameters such as name, uuid, disk path, and MAC address:\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; Restart the service and start the new virtual machine:\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server2 [root@cloud ~]# virsh list --all V. KVM Network Configuration KVM commonly supports three network modes: NAT mode, Host-Only mode, and Bridge mode.\n1. NAT Mode NAT is implemented through iptables address masquerading:\n1 # iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE Or use SNAT to specify the source address:\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 View NAT rules:\n1 [root@cloud ~]# iptables -t nat -nL The default network configuration file is located at /etc/libvirt/qemu/networks:\n1 2 3 [root@cloud networks]# pwd /etc/libvirt/qemu/networks [root@cloud networks]# vim default.xml 2. Host-Only Mode In Host-Only mode, virtual machines can only communicate with the host machine and cannot access external networks.\n3. Enabling KVM Virtual Machines to Access the Internet (1) Add a network interface to the VMware host that can access the Internet.\n(2) Enter the host operating system and configure dual bridges.\nifcfg-br0 (internal network bridge):\n1 2 3 4 5 6 TYPE=Bridge NAME=br0 DEVICE=br0 ONBOOT=yes IPADDR=192.168.8.99 PREFIX=24 ifcfg-br1 (external network bridge, DHCP):\n1 2 3 4 5 TYPE=Bridge NAME=br1 DEVICE=br1 ONBOOT=yes BOOTPROTO=dhcp ifcfg-ens33 bridged to br0:\n1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 ifcfg-ens37 bridged to br1:\n1 2 3 4 5 TYPE=Ethernet NAME=ens37 DEVICE=ens37 ONBOOT=yes BRIDGE=br1 Restart the service and shut down the virtual machines:\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 Modify the network interface of server1 in KVM to bridge to br1, and change the IP acquisition method to DHCP inside VM1:\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 Test external network connectivity:\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. Installing KVM Virtual Machines via Command Line With kickstart automated installation scripts, you can perform unattended batch installation of virtual machines.\n1. Create a Disk 1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server3.qcow2 10g 2. Automated Installation Use virt-install to specify the installation source and kickstart file:\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 Example kickstart configuration file 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 Note: The installation source addresses (192.168.0.107), hostname (server3), and the password hashes after rootpw and user in the above example are for illustration only. Please replace them with your own environment\u0026rsquo;s addresses and generated password ciphertexts in actual use.\n","permalink":"https://LMDAWN1127.github.io/en/posts/kvm-virtualization-guide/","summary":"A systematic introduction to KVM virtualization technology, covering development history, deployment methods, virtual machine management, network mode configuration, and automated VM installation using the command line.","title":"KVM Virtualization from Beginner to Practice: Deployment, Management, and Network Configuration"},{"content":"1. Choose the Right Base Image 1 2 3 4 5 6 7 8 9 10 # ❌ Too large FROM ubuntu:22.04 # ~77MB FROM python:3.11 # ~1GB # ✅ Recommended FROM python:3.11-alpine # ~50MB FROM python:3.11-slim # ~130MB # ✅ Smallest FROM gcr.io/distroless/python3 # ~20MB Base image Size ubuntu:22.04 ~77MB python:3.11 ~1GB python:3.11-alpine ~50MB distroless ~20MB 2. Use Multi-Stage Builds The most effective technique: compile in a large image, ship the artifact from a minimal one.\n1 2 3 4 5 6 7 8 9 10 11 # Stage 1: compile FROM golang:1.21 AS builder WORKDIR /app COPY . . RUN go build -o myapp # Stage 2: runtime FROM alpine:3.18 WORKDIR /app COPY --from=builder /app/myapp . CMD [\u0026#34;./myapp\u0026#34;] A Go app goes from ~1GB to ~15MB.\n3. Combine RUN Instructions 1 2 3 4 5 6 7 8 9 10 11 12 # ❌ Multiple layers RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y wget RUN rm -rf /var/lib/apt/lists/* # ✅ Combined into one layer RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends \\ curl \\ wget \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* 4. Clean Up Build Cache 1 2 3 4 5 6 7 8 9 # Python — disable pip cache RUN pip install --no-cache-dir -r requirements.txt # Node.js — clear npm cache RUN npm ci --only=production \u0026amp;\u0026amp; \\ npm cache clean --force # Go — strip debug info RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 5. Use .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 # Check context size docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; 6. Install Only What You Need 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # ❌ Build tools left in the runtime image 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;] # ✅ Build and runtime separated 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. Use \u0026ndash;no-install-recommends 1 2 3 4 5 # ❌ Installs all recommended packages RUN apt-get install -y curl # ✅ Install only what\u0026#39;s required (~50% fewer dependencies) RUN apt-get install -y --no-install-recommends curl 8. Compress Static Assets 1 2 3 4 5 6 7 8 9 10 11 # Minify JavaScript RUN uglifyjs app.js -o app.min.js # Minify CSS RUN cssnano app.css app.min.css # Optimize images RUN find . -name \u0026#34;*.png\u0026#34; -exec optipng {} \\; # Strip debug info from Go binaries RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 9. Use Squash to Merge Layers 1 2 # Merge all layers into one, removing duplicate files from intermediate layers docker build --squash -t myapp:slim . Requires the experimental feature to be enabled in /etc/docker/daemon.json.\n10. Analyze Image Layers 1 2 3 4 5 6 7 # Use dive to analyze each layer\u0026#39;s size and content docker run --rm -it \\ -v /var/run/docker.sock:/var/run/docker.sock \\ wagoodman/dive myapp:latest # Or use docker history docker history myapp:latest Real-World Case: Python App 1 2 3 4 5 6 # ❌ Before optimization (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 # ✅ After optimization (150MB, 87% smaller) 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;] Real-World Case: Node.js App 1 2 3 4 5 6 # ❌ Before optimization (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 # ✅ After optimization (180MB, 84% smaller) 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;] Recommended Tools Tool Purpose dive Analyze each layer\u0026rsquo;s content and size docker-slim Automatically shrink images hadolint Dockerfile static analysis trivy Container security scanning ","permalink":"https://LMDAWN1127.github.io/en/posts/dockerfile-smaller-image/","summary":"Big Docker images slow down deployments and inflate storage costs? This article shares 10 proven techniques to shrink your image by more than 90%.","title":"Docker Image Too Large? 10 Tips to Shrink It by 90%"},{"content":"1. Order Instructions to Leverage Cache Put rarely-changing instructions first, frequently-changing ones last. Once a layer\u0026rsquo;s cache is invalidated, every subsequent layer is rebuilt.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ Wrong: reinstall dependencies on every code change FROM node:18-alpine WORKDIR /app COPY . . # code changes often RUN npm ci --production # deps rarely change CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # ✅ Right: reinstall deps only when they change FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # dep definitions (rarely change) RUN npm ci --production # install deps (rarely change) COPY . . # source code (changes often) CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 # Check cache hits docker build -t myapp . 2\u0026gt;\u0026amp;1 | grep -E \u0026#34;(CACHED|Using cache)\u0026#34; 2. Use .dockerignore to Shrink the Build Context docker build sends the entire directory to the Docker daemon, including .git, node_modules, and other files you don\u0026rsquo;t need.\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 # Check context size docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; # Before: Sending build context to Docker daemon 1.2GB # After: Sending build context to Docker daemon 50MB 3. Use Lightweight Base Images Base image Size Notes ubuntu:22.04 ~77MB Full Ubuntu python:3.11 ~1GB Full Python python:3.11-slim ~130MB Stripped down python:3.11-alpine ~50MB Alpine-based 1 2 3 4 5 6 7 8 # ❌ Too large FROM python:3.11 # 1GB # ✅ Recommended FROM python:3.11-slim # 130MB # ✅ Smallest (watch out for musl compatibility) FROM python:3.11-alpine # 50MB Alpine uses musl instead of glibc, which can break some software. If you hit issues, fall back to slim.\n4. Combine RUN Instructions to Reduce Layers Every RUN creates a new layer — more layers means slower builds and larger images.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ 5 layers 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 layer 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 # Check image layer count docker history myapp:latest 5. Use Multi-Stage Builds Build in one stage, run in another, and copy only what\u0026rsquo;s needed.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Stage 1: build 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 . # Stage 2: runtime (only ~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;] Approach Image size Build time Single-stage ~1.2GB 5 min Multi-stage ~15MB 2 min 6. Use BuildKit to Speed Up Builds BuildKit supports parallel builds and advanced caching.\n1 2 3 4 5 6 7 # Enable BuildKit export DOCKER_BUILDKIT=1 # Or enable it permanently in /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 # Use cache mounts (a BuildKit feature) # syntax=docker/dockerfile:1 FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # Cache mount avoids re-downloading deps every build 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 # Check whether BuildKit is enabled docker build --progress=plain -t myapp . 2\u0026gt;\u0026amp;1 | head -5 7. Use Cache Import/Export In CI/CD, the cache is lost after every build. Cache import/export keeps it across builds.\n1 2 3 4 5 6 7 8 9 10 11 # Export to a local directory docker build \\ --cache-from type=local,src=/tmp/cache \\ --cache-to type=local,dest=/tmp/cache \\ -t myapp . # Use a registry as cache backend 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 example - 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. Install Dependencies in Parallel 1 2 3 4 5 6 7 8 9 10 11 12 # Node.js — npm ci is faster than npm install RUN npm ci --production # Python — disable pip cache RUN pip install --no-cache-dir -r requirements.txt # Go — download modules in parallel RUN go mod download # Rust — use cargo-chef COPY --from=chef /app/recipe.json recipe.json RUN cargo chef cook --release --recipe-path recipe.json 9. Clean Up Build Cache and Temp Files Clean up in the same RUN instruction to avoid leaving files in earlier layers.\n1 2 3 4 5 6 7 8 9 10 11 12 # ✅ Right: clean up in the same layer 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/* # ❌ Wrong: cleanup in another layer — files still exist RUN apt-get update \u0026amp;\u0026amp; apt-get install -y build-essential RUN make \u0026amp;\u0026amp; make install RUN apt-get clean # too late! earlier layers still contain the files Per-language cleanup commands:\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. Pin a Specific Version Tag 1 2 3 4 5 6 7 8 # ❌ latest may change, cache is unpredictable FROM node:latest # ✅ Specific version FROM node:18.17.0-alpine3.18 # ✅ Safest: use the SHA256 digest FROM node:18.17.0-alpine3.18@sha256:abc123... How Layers Work 1 2 # View image layer history docker history \u0026lt;image-name\u0026gt; Instructions that create new layers: FROM, RUN, COPY, ADD\nInstructions that don\u0026rsquo;t create new layers (just metadata): CMD, ENTRYPOINT, ENV, EXPOSE, WORKDIR, USER, LABEL, ARG, VOLUME\n1 2 3 4 5 6 7 8 9 10 11 # This creates 4 layers FROM node:18-alpine # layer 1 RUN apk add --no-cache curl # layer 2 COPY package.json . # layer 3 RUN npm install # layer 4 # These instructions add no layer WORKDIR /app # no layer ENV NODE_ENV=production # no layer EXPOSE 3000 # no layer CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # no layer Before / After Comparison 1 2 3 4 5 6 7 8 9 # ❌ Before optimization (8 min, 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 # ✅ After optimization (1.5 min, 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;] Metric Before After Improvement Build time 8 min 1.5 min 81% ⬇️ Image size 1.2GB 150MB 87% ⬇️ Context size 500MB 50MB 90% ⬇️ Cache hit rate 10% 90% 80% ⬆️ Monitoring and Debugging 1 2 3 4 5 6 7 8 9 10 11 # Show detailed build time time docker build -t myapp . # BuildKit detailed progress DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp . # Analyze image layers docker history myapp:latest # Use dive for deep layer analysis dive myapp:latest ","permalink":"https://LMDAWN1127.github.io/en/posts/dockerfile-faster-build/","summary":"Docker builds too slow? This article shares 10 proven optimization tips, from cache strategy to multi-stage builds, to help you cut build time dramatically.","title":"How to Make Dockerfile Builds Faster: 10 Practical Optimization Tips"},{"content":"1. Basic Syntax The syntax is almost identical, but the behavior is very different:\n1 2 3 4 5 # COPY - pure file copy, no extra processing COPY [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; # ADD - supports auto-extraction and remote URLs, but with unpredictable behavior ADD [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; 2. Auto-Extracting Archives ADD automatically extracts local archives, while COPY does not:\n1 2 3 4 5 6 7 # ADD auto-extracts .tar.gz ADD app.tar.gz /opt/app/ # Result: contents are extracted into /opt/app/ # COPY only copies the file itself COPY app.tar.gz /opt/app/ # Result: /opt/app/app.tar.gz remains an archive Supported archive formats: .tar, .tar.gz / .tgz, .tar.bz2, .tar.xz\nIf you want to copy an archive without extracting it, you must use COPY.\n3. Remote URL Downloads ADD can download files directly from a URL; COPY does not:\n1 2 3 4 5 # ADD can fetch a remote file (not recommended) ADD https://example.com/app.tar.gz /opt/ # COPY does not support URLs COPY https://example.com/app.tar.gz /opt/ # Error! Prefer RUN + curl over ADD for remote downloads: ADD downloads don\u0026rsquo;t benefit from build cache, can\u0026rsquo;t handle errors, and don\u0026rsquo;t verify integrity.\n1 2 3 4 # Recommended: 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. Auto-Creating Directories Both automatically create missing intermediate directories:\n1 2 3 4 5 6 # If /etc/myapp/config/ doesn\u0026#39;t exist, it will be created automatically COPY app.conf /etc/myapp/config/ ADD app.conf /etc/myapp/config/ # No need for manual mkdir # RUN mkdir -p /etc/myapp/config ← unnecessary 5. Build Cache Behavior 1 2 3 4 5 # COPY - strictly matches file content; cache is only invalidated when content changes COPY config/app.conf /etc/app/ # ADD - cache stays valid as long as the archive content is the same ADD app.tar.gz /opt/app/ Scenario COPY ADD File content changes Cache invalidated Cache invalidated File metadata changes Cache invalidated Cache invalidated Same archive content N/A Cache valid 6. Feature Comparison Feature COPY ADD Copy local files ✅ ✅ Copy directories ✅ ✅ Wildcard support ✅ ✅ Auto-create directories ✅ ✅ Auto-extract archives ❌ ✅ Remote URL support ❌ ✅ Clarity of intent ✅ High ❌ Low Recommended ✅ Default ⚠️ Specific cases 7. When to Use Each Use COPY for:\n1 2 3 4 5 6 7 8 9 # Copy config files COPY nginx.conf /etc/nginx/nginx.conf # Copy application code COPY src/ /app/src/ COPY package.json /app/ # Copy a binary from a multi-stage build COPY --from=builder /app/build/app /usr/local/bin/ Use ADD only when auto-extraction is needed:\n1 2 3 4 5 # Reasonable use of ADD ADD node-v18.17.0-linux-x64.tar.gz /usr/local/ # Don\u0026#39;t use ADD for regular files ADD app.conf /etc/app/ # ← use COPY instead 8. Best Practices 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # Rule 1: default to COPY COPY requirements.txt /app/ # ✅ ADD requirements.txt /app/ # ❌ unnecessary # Rule 2: use ADD only when extraction is needed ADD node-v18.tar.gz /usr/local/ # ✅ extraction needed # Rule 3: use RUN + curl for remote files 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/ # ❌ # Rule 4: use .dockerignore to shrink the build context # .dockerignore # .git # node_modules # *.md # .env # Rule 5: order COPY statements to leverage cache COPY package.json package-lock.json ./ # dependencies first (rarely change) RUN npm ci --production COPY . . # source code last (changes often) 9. Common Pitfalls 1 2 3 4 5 6 7 8 9 10 # Pitfall 1: unexpected auto-extraction ADD app.tar.gz /opt/app/ # You expected a file copy, but got an extracted directory # Pitfall 2: remote files can\u0026#39;t be cached ADD https://github.com/user/repo/releases/download/v1.0/app.tar.gz /tmp/ # Every build re-downloads # Pitfall 3: ownership issues COPY --chown=app:app src/ /app/src/ # specify the owner explicitly 10. Decision Tree 1 2 3 4 5 6 7 Need to copy a file? ├── Local archive that needs extraction? │ └── Use ADD ├── Local regular file? │ └── Use COPY └── Remote file? └── Use RUN + curl ","permalink":"https://LMDAWN1127.github.io/en/posts/dockerfile-copy-vs-add/","summary":"Both COPY and ADD can copy files, but they differ in fundamental ways. This article breaks down the differences and helps you write better Dockerfiles.","title":"COPY vs ADD in Dockerfile: A Detailed Comparison"},{"content":"1. Requirements OS: CentOS 7 / 8 / Stream 9 Node.js: v18 or higher RAM: 2 GB or more recommended Network: Access to the Anthropic API 2. Install Node.js 1 2 3 4 5 6 7 8 9 # Add the NodeSource repository curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash - # Install Node.js sudo yum install -y nodejs # Verify node -v npm -v If the Node.js version is too old, use nvm to manage versions:\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. Install Claude Code 1 2 3 4 5 # Global install npm install -g @anthropic-ai/claude-code # Verify claude --version Two ways to fix permission issues:\n1 2 3 4 5 6 7 8 # Option 1: sudo sudo npm install -g @anthropic-ai/claude-code # Option 2: change the npm global prefix (recommended) 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. Configure the API Key 1 2 3 4 5 6 # Option 1: environment variable (recommended) echo \u0026#39;export ANTHROPIC_API_KEY=\u0026#34;your-api-key-here\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc # Verify echo $ANTHROPIC_API_KEY Option 2: just run claude — it will prompt you for the API key on first launch.\n5. Basic Usage 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Start an interactive session cd /path/to/your/project claude # Ask a question directly (non-interactive) claude -p \u0026#34;Write a Python script that counts the number of files in the current directory\u0026#34; # Analyze code cat main.py | claude -p \u0026#34;Analyze potential issues in this code\u0026#34; # View or edit configuration claude config # Update to the latest version claude update Command Description claude Start an interactive session claude -p \u0026quot;question\u0026quot; Ask directly in non-interactive mode claude config View or edit configuration claude update Update to the latest version 6. Network Troubleshooting If connecting to the Anthropic API times out:\n1 2 3 4 5 6 # Configure a proxy export https_proxy=\u0026#34;http://your-proxy:port\u0026#34; export http_proxy=\u0026#34;http://your-proxy:port\u0026#34; # Test connectivity curl -s https://api.anthropic.com/ | head -5 7. Security Tips 1 2 3 4 5 6 7 8 9 # Never hard-code the API key — always use an environment variable 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 written in code # ❌ # Restrict file permissions chmod 600 ~/.bashrc # Check for leaks 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/en/posts/centos-deploy-claude-code/","summary":"Claude Code is Anthropic\u0026rsquo;s CLI tool that lets you interact with Claude AI directly from the terminal to complete coding tasks. This guide walks through the full deployment process on CentOS.","title":"CentOS Deployment Guide for Claude Code: Full Walkthrough"},{"content":"flannel in Action: Building Docker Cross-Host Container Networks from Scratch I. Network Environment Planning etcd host IP: 192.168.8.88 hostname: docker-network Host1 IP: 192.168.8.188 hostname: web01 Host2 IP: 192.168.8.99 hostname: web02 II. etcd Host Configuration Step 1: Download and Extract etcd Download etcd-v3.6.0-linux-amd64.tar.gz package from github.com Copy the package to the etcd host /root directory and extract: 1 [root@docker-network ~]# tar -zxvf etcd-v3.6.0-linux-amd64.tar.gz Step 2: Install etcd Executable Files 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/ Step 3: Create Data Directory 1 [root@docker-network ~]# mkdir /var/lib/etcd Step 4: Configure and Start etcd Service Create etcd.service file: 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 Reload system service configuration and start etcd: 1 2 3 [root@docker-network ~]# systemctl daemon-reload [root@docker-network ~]# systemctl enable etcd.service [root@docker-network ~]# systemctl restart etcd.service Verify etcd listening status: 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 Step 5: Add flannel Network Configuration to etcd 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; The above content is all on one line. Verify that the configuration was written successfully\n1 etcdctl --endpoints http://192.168.8.88:2379 get /coreos.com/network/config Configuration description:\nNetwork: Flannel address pool SubnetLen: Subnet mask length assigned to a single host\u0026rsquo;s docker0 (24 bits means 255.255.255.0) SubnetMin/SubnetMax: Allocatable subnet range (example is 172.16.1.0/24 to 172.16.10.0/24) Backend: Data forwarding mode (vxlan supports cross-host communication) Step 6: Disable Firewall 1 2 [root@docker-network ~]# systemctl disable firewalld [root@docker-network ~]# systemctl stop firewalld III. web01 Host Configuration (Host1: 192.168.8.188) Step 1: Download and Extract flannel Download flannel-v0.26.7-linux-amd64.tar.gz to the host Extract the file: 1 [root@web01 ~]# tar -zxvf flannel-v0.26.7-linux-amd64.tar.gz Step 2: Install flannel Executable Files 1 [root@web01 ~]# cp -p flanneld mk-docker-opts.sh /usr/local/bin/ Step 3: Configure and Start flannel Service Create flanneld.service file: 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 Parameter description:\n--iface=192.168.8.188: Specify the local IP for connecting to etcd --etcd-endpoints: etcd server address Reload service configuration and start flannel:\n1 2 3 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl enable flanneld.service [root@web01 ~]# systemctl restart flanneld.service Step 4: Verify flannel Network Interface 1 [root@web01 ~]# ifconfig Should display flannel.1 interface (example output):\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 Step 5: Modify Docker Configuration File View subnet environment variables generated by flannel: 1 [root@web01 ~]# cat /run/flannel/subnet.env Example output:\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 Example output:\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; Modify docker service configuration: 1 [root@web01 ~]# vim /usr/lib/systemd/system/docker.service Add to the [Service] section:\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 Restart docker service: 1 2 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl restart docker.service Step 6: Verify Docker Network Create nginx container and enter it: 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;container_id\u0026gt; /bin/bash View container IP (example is 172.16.5.3): 1 2 root@container_id:/# ifconfig eth0: inet 172.16.5.3 netmask 255.255.255.0 IV. web02 Host Configuration (Host2: 192.168.0.208) Step Description Configuration steps are identical to web01, only need to modify the following parameters: --iface=192.168.8.99 (local IP) Docker container IP example: 172.16.5.2 (automatically assigned by flannel) V. Experiment Verification Ping web02 container IP (example: 172.16.9.2) from web01 container:\nCreate a container in web02 and view the container IP address\nResult: Inter-container communication successful, flannel network configuration complete.\nAppendix: etcd Network Configuration Query 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/en/posts/flannel-network-lab/","summary":"\u003ch1 id=\"flannel-in-action-building-docker-cross-host-container-networks-from-scratch\"\u003eflannel in Action: Building Docker Cross-Host Container Networks from Scratch\u003c/h1\u003e\n\u003ch2 id=\"i-network-environment-planning\"\u003eI. Network Environment Planning\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003eetcd host IP: 192.168.8.88 hostname: docker-network\u003c/li\u003e\n\u003cli\u003eHost1 IP: 192.168.8.188 hostname: web01\u003c/li\u003e\n\u003cli\u003eHost2 IP: 192.168.8.99 hostname: web02\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"ii-etcd-host-configuration\"\u003eII. etcd Host Configuration\u003c/h2\u003e\n\u003ch3 id=\"step-1-download-and-extract-etcd\"\u003eStep 1: Download and Extract etcd\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003eDownload etcd-v3.6.0-linux-amd64.tar.gz package from github.com\u003c/li\u003e\n\u003cli\u003eCopy the package to the etcd host /root directory and extract:\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=\"step-2-install-etcd-executable-files\"\u003eStep 2: Install etcd Executable Files\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=\"step-3-create-data-directory\"\u003eStep 3: Create Data Directory\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=\"step-4-configure-and-start-etcd-service\"\u003eStep 4: Configure and Start etcd Service\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003eCreate etcd.service file:\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\u003eReload system service configuration and start 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\u003eVerify etcd listening status:\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=\"step-5-add-flannel-network-configuration-to-etcd\"\u003eStep 5: Add flannel Network Configuration to 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\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\u003cblockquote\u003e\n\u003cp\u003eThe above content is all on one line. Verify that the configuration was written successfully\u003c/p\u003e","title":"flannel in Action: Building Docker Cross-Host Container Networks from Scratch"}]