当前位置: 首页 > ds >正文

Ansible 管理变量和事实

Ansible 管理变量和事实

实验环境

[wsh@controller ansible ✔]$ pwd
/home/wsh/ansible
[wsh@controller ansible ✔]$ ls
ansible.cfg  inventory
[wsh@controller ansible ✔]$ cat ansible.cfg inventory 
[defaults]
inventory = ./inventory
remote_user = wsh[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False
[controllers]
controller[nodes]
node1
node2
node3
node4

管理 VARIABLES

变量简介

ansible 利用变量来存储数据,以便在Ansible项目文件中重复引用,有利于简化项目的创建和维护,降低出错率。我们在playbook中可以针对如用户、软件包、服务、文件等进行变量定义。

变量命名规则

  • 只能包含字母、数字和下划线(如包含空格、点、$符号都为非法变量名)
  • 只能以字母开头

变量范围和优先级

ansible项目文件中多个位置支持定义变量,主要包含三个基本范围:

  • Global scope:从命令行或 Ansible 配置设置的变量。
  • Play scope:在play和相关结构中设置的变量。
  • Host scope:由清单、事实(fact)收集或注册的任务,在主机组和个别主机上设置的变量。

优先级从高到低顺序:Global -> Play -> Host

在多个级别上定义了相同名称的变量,则采用优先级别最高的变量。

Global scope

通过选项-e传递给ansible或者ansible-playbook命令。

[wsh@controller ansible ✔]$ ansible node1 -m debug -a 'msg={{ test }}' -e 'test=httpd'
node1 | SUCCESS => {"msg": "httpd"
}[wsh@controller ansible ✘]$ ansible node1 -m yum -a 'name={{ test }} state=present' -e 'test=httpd'
node1 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "changes": {"installed": ["httpd"]}, 
······

Play scope

vars 声明

格式1:

---
- name: test vars statement in playhosts: node1vars: #vars种声明的变量可以在后续使用user: joehome: /home/joetasks:- name: add user {{ user }}user:name: "{{ user }}"home: "{{ home }}"state: present- name: debug userdebug:msg: |username is {{ user }}home is {{ home }}

格式2:

ansible 2.18 版本中移除 列表格式变量。

---
- name: test vars statement in playhosts: node1vars: #通过 `-` 排列- user: joe- home: /home/joetasks:- name: add user {{ user }}user:name: "{{ user }}"home: "{{ home }}"state: present- name: debug userdebug:msg: |username is {{ user }}home is {{ home }}
vars_files 声明

如果变量比较多,我么可以使用变量文件进行分类,然后分列别应用到playbook中。

示例:

---
- name: test vars statement in playhosts: node1vars_files: #变量位置以playbook所处目录为相对路径- vars/user1.yamltasks:- name: add user {{ user }}user:name: "{{ user }}"home: "{{ home }}"state: present- name: debug userdebug:msg: >username is {{ user}}home is {{ home }}
[wsh@controller ansible ✔]$ mkdir vars
[wsh@controller ansible ✔]$ vim vars/user1.yaml
# yaml 格式
user: user1
home: /home/user1
变量引用

可将变量名称放在双花括号( {{ }} )内引用变量。在任务执行时, Ansible会将变量替换为其值。

当变量用作值的第一元素时,变量引用必须使用引号(单引号或者双引号),否则会报错。

错误的示例:

---
- name: test variables playhosts: node1vars:user: joehome: /home/joetasks:- name: create useruser:name: {{ user }} #缺少引号state: present

验证:

[wsh@controller ansible ✔]$ ansible-playbook test.yml 
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: No JSON object could be decodedSyntax Error while loading YAML.found unacceptable key (unhashable type: 'AnsibleMapping')The error appears to be in '/home/wsh/ansible/test.yml': line 10, column 16, but may
be elsewhere in the file depending on the exact syntax problem.The offending line appears to be:user:name: {{ user }} #缺少引号^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:with_items:- {{ foo }}Should be written as:with_items:- "{{ foo }}"

在这里插入图片描述

Host scope

主机变量应用于主机和主机组。主机变量优先级高于主机组变量。

主机清单中定义

较旧的做法是直接在清单文件中定义。不建议采用,但仍可能会遇到。

示例

[servers]
node1 user=wsh
node2[servers:vars]
user=wsh

验证:

[wsh@controller ansible ✔]$ ansible servers -m debug -a 'var=user'
node1 | SUCCESS => {"user": "wsh"
}
node2 | SUCCESS => {"user": "wsh"
}

缺点:使得清单文件更复杂,在同一文件中混合提供了主机和变量信息。

目录分层结构定义

在项目目录中创建如下目录:

  • group_vars,定义主机组变量。目录中文件名可以直接使用 主机组名 或者 主机组名.yaml

  • host_vars,定义主机变量。目录中文件名可以直接使用 主机名 或者 主机名.yaml

示例1:

[wsh@controller ansible ✔]$ tail inventory 
[nodes]
node1
node2
node3
node4[servers]
node1
node2[wsh@controller ansible ✔]$ mkdir group_vars
[wsh@controller ansible ✔]$ vim group_vars/servers.yml
user: wsh[wsh@controller ansible ✔]$ mkdir host_vars
[wsh@controller ansible ✔]$ vim host_vars/node1.yml
user: wym

验证:

[wsh@controller ansible ✔]$ ansible servers -m debug -a 'var=user'
node1 | SUCCESS => {"user": "wym"
}
node2 | SUCCESS => {"user": "wsh"
}

目录结构定义主机和主机组的变量是首选做法

示例2

[wsh@controller ansible ✔]$ tree
.
├── ansible.cfg
├── group_vars
│   ├── dc
│   ├── dc1
│   └── dc2
├── host_vars
│   └── node1.yml
├── inventory
├── test.yml
└── vars3 directories, 7 files[wsh@controller ansible ✔]$ vim inventory
[dc1]
node1
node2[dc2]
node3
node4[dc:children]
dc1
dc2[wsh@controller ansible ✔]$ grep . group_vars/* host_vars/*
group_vars/dc:package: httpd
group_vars/dc1:package: httpd
group_vars/dc2:package: apache
host_vars/node1.yml:package: mariadb-server[wsh@controller ansible ✔]$ ansible all -m debug -a 'var=package'
controller | SUCCESS => {"package": "VARIABLE IS NOT DEFINED!"
}
node1 | SUCCESS => {"package": "mariadb-server"
}
node2 | SUCCESS => {"package": "httpd"
}
node4 | SUCCESS => {"package": "apache"
}
node3 | SUCCESS => {"package": "apache"
}
主机连接特殊变量

详情参考:主机连接特殊变量。

  • ansible_connection,与主机的连接类型,可以是 smart、ssh 或 paramiko。默认为smart。

  • ansible_host,要连接的主机的名称,默认值就是主机清单名称。

  • ansible_port,ssh 端口号,如果不是 22。

  • ansible_user,ssh 用户名。

  • ansible_ssh_pass,要使用的 ssh 密码。切勿以纯文本形式存储此变量,始终使用保管库。

  • ansible_ssh_private_key_file,ssh 使用的私钥文件。如果使用多个密钥并且您不想使用 SSH 代理,这很有用。

  • ansible_ssh_common_args,此设置始终附加到 sftp、scp 和 ssh 的默认命令行。

  • ansible_sftp_extra_args,此设置始终附加到默认的 sftp 命令行。

  • ansible_scp_extra_args,此设置始终附加到默认的 scp 命令行。

  • ansible_ssh_extra_args,此设置始终附加到默认的 ssh 命令行。

  • ansible_become,等效于 ansible_sudo 或 ansible_su,允许强制提权。

  • ansible_become_method,允许设置权限提升方法。

  • ansible_become_user,等效于 ansible_sudo_user 或 ansible_su_user,允许设置您通过权限升级成为的用户。

  • ansible_become_pass,等效于 ansible_sudo_pass 或 ansible_su_pass,允许您设置权限提升密码(切勿以纯文本形式存储此变量;始终使用保管库。请参阅变量和保管库)。

数组变量

除了将与同一元素相关的配置数据(软件包列表、服务列表和用户列表等)分配到多个变量外,管理员也可以使用数组变量,将多个值存储在同一变量中。

示例:

user1_first_name: Bob
user1_last_name: Jones
user1_home_dir: /users/bjones
user2_first_name: Anne
user2_last_name: Cook
user2_home_dir: /users/acook

改写如下:

users:bjones:first_name: Boblast_name: Joneshome_dir: /users/bjonesacook:first_name: Annelast_name: Cookhome_dir: /users/acook

数组变量引用方式一:

# Returns 'Bob'
users.bjones.first_name
# Returns '/users/acook'
users.acook.home_dir

数组变量引用方式二:

# Returns 'Bob'
users['bjones']['first_name']
# Returns '/users/acook'
users['acook']['home_dir']

引用方式总结:

  • 如果使用方法一**.分隔符**引用的关键字与python的功能函数同名,例如discard、copy、add,那么就会出现问题。方法二[‘’]引用方式可以避免这种错误。
  • 尽管两种方法都可以使用,为了减少排故难度,Ansible中统一使用其中一种方法。

示例1:

---
- name: test vars statement in playhosts: node1vars: users:laoma:user_name: laomahome_path: /home/laomalaowang:user_name: laowanghome_path: /home/laowangtasks:- name: add user {{ users.laoma.user_name }}user:name: '{{ users.laoma.user_name }}'home: "{{ users.laoma.home_path }}"- name: debug laowangdebug: msg: >username is {{ users['laowang']['user_name'] }}home_path is {{ users['laowang']['home_path'] }}

示例2:

---
- name: test vars statement in playhosts: node1vars: users:- user_name: laoma1home_path: /home/laoma1- user_name: laoma2home_path: /home/laoma2tasks:- name: add user {{ users.0.user_name }}user:name: "{{ users.0.user_name }}"home: "{{ users.0.home_path }}"- name: debug {{ users[1].user_name }}debug: msg: "{{ users[1].user_name }}"

register 语句

**register 语句捕获任务输出。**输出保存在一个临时变量中,稍后在playbook中可用于调试用途或者达成其他目的。

示例:

---
- name: Installs a package and prints the resulthosts: node1tasks:- name: Install the packageyum:name: httpdstate: installedregister: install_result- debug: var: install_result

MAGIC 变量

magic 变量由 Ansible 自动设置,可用于获取与特定受管主机相关的信息。

假设当前清单内容为:

controller[webs]
node1
node2[dbs]
node3
node4

最常用四个 Magic 变量:

  • inventory_hostname,包含清单中配置的当前受管主机的主机名称。这可能因为各种原因而与FACTS报告的主机名称不同。

    [wsh@controller ansible ✔]$ ansible node1 -m debug -a 'var=inventory_hostname'
    node1 | SUCCESS => {"inventory_hostname": "node1"
    }
    
  • group_names,列出当前受管主机所属的所有主机组。

    [wsh@controller ansible ✔]$ ansible node1 -m debug -a 'var=group_names'
    node1 | SUCCESS => {"group_names": ["webs"]
    }
    
  • groups,列出清单中的所有组,以及组中含有的主机。

    [wsh@controller ansible ✔]$ ansible node1 -m debug -a 'var=groups'
    node1 | SUCCESS => {"groups": {"all": ["controller", "node1", "node2", "node3", "node4"], "dbs": ["node3", "node4"], "ungrouped": ["controller"], "webs": ["node1", "node2"]}
    }
    
  • hostvars,包含所有受管主机的变量,可用于获取另一台受管主机的变量的值。如果还没有为受管主机收集FACTS,则它不会包含该主机的 FACTS。

    例如:hostvars.controller.group_names

管理 SECRETS

Ansible Vault 简介

Ansible可能需要访问密码或API密钥等敏感数据,此信息可能以纯文本形式存储在清单变量或其他Ansible文件中。任何有权访问Ansible文件的用户或存储这些Ansible文件的版本控制系统都能够访问此敏感数据。

这显然存在安全风险。Ansible随附的 Ansible Vault 可以加密任何由Ansible使用的结构化数据文件,包括清单变量、playbook中含有的变量文件、在执行playbook时作为参数传递的变量文件,以及Ansible角色中定义的变量。

ansible-vault 命令

[wsh@controller ansible ✔]$ ansible-vault -h
usage: ansible-vault [-h] [--version] [-v]{create,decrypt,edit,view,encrypt,encrypt_string,rekey}...encryption/decryption utility for Ansible data filespositional arguments:{create,decrypt,edit,view,encrypt,encrypt_string,rekey}create              Create new vault encrypted filedecrypt             Decrypt vault encrypted fileedit                Edit vault encrypted fileview                View vault encrypted fileencrypt             Encrypt YAML fileencrypt_string      Encrypt a stringrekey               Re-key a vault encrypted fileoptional arguments:--version             show program's version number, config file location,configured module search path, module location,executable location and exit-h, --help            show this help message and exit-v, --verbose         verbose mode (-vvv for more, -vvvv to enableconnection debugging)See 'ansible-vault <command> --help' for more information on a specific
command.

ansible-vault create和edit命令使用默认编辑器vi打开文件。您可以设置和导出EDITOR环境变量指定其他默认编辑器。

例如,若要将默认编辑器设为vim, 可设置为export EDITOR=vim

[wsh@controller ansible ✔]$ export EDITOR=vim
# 或者添加到bash配置文件中
[wsh@controller ansible ✔]$ echo 'export EDITOR=vim' >> ~/.bashrc
[wsh@controller ansible ✔]$ source ~/.bashrc# 创建加密文件,内容是yaml格式,例如 password: redhat
[wsh@controller ansible ✔]$ ansible-vault create secret.yml
New Vault password: `000000`
Confirm New Vault password: `000000`
password: 000000[wsh@controller ansible ✔]$ cat secret.yml 
$ANSIBLE_VAULT;1.1;AES256
35303562313762326666666365326662666634646539646533656263313535656533363964313037
3530623562303030353735623031326564643931316439330a653935626531356331656333386334
32366139653231393837613032363433366435336339363536653434623739383263306139333830
3965653139623436390a366564663532393762653266373133373635626466643462323664393735
36653433343539333833366263393666656562396633353233653632336564663636# 查看加密文件
[wsh@controller ansible ✔]$ ansible-vault view secret.yml 
Vault password: `000000`
password: 000000# 使用 --vault-password-file 选项从文件中读取加密和解密密码
[wsh@controller ansible ✔]$ echo '000000' > passwd
[wsh@controller ansible ✔]$ ansible-vault view secret.yml --vault-password-file=passwd
password: 000000# 还可以在ansible.cfg的defaults中设置vault_password_file
[wsh@controller ansible ✔]$ vim ansible.cfg 
vault_password_file = ./pass# 编辑加密文件
[wsh@controller ansible ✔]$ ansible-vault edit secret.yml --vault-password-file=passwd
password: wsh# 解密文件
[wsh@controller ansible ✔]$ ansible-vault decrypt secret.yml --vault-password-file=passwd
Decryption successful
[wsh@controller ansible ✔]$ cat secret.yml 
password: wsh# 加密文件
[wsh@controller ansible ✔]$ ansible-vault encrypt secret.yml --vault-password-file=passwd
Encryption successful# 更改加密文件密码
[wsh@controller ansible ✔]$ ansible-vault rekey secret.yml --vault-password-file=passwd
New Vault password: `wsh`
Confirm New Vault password: `wsh`
Rekey successful# 还可以使用选项 --new-vault-password-file 指定新密码所在文件位置
[wsh@controller ansible ✔]$ ansible-vault rekey secret.yaml --vault-password-file=pass --new-vault-password-file=pass-new
Rekey successful

综合案例

playbook.yml 内容如下:

---
- name: config mariadb serverhosts: node1tasks:- name: install mariadb-serveryum:name: - mariadb-server- python2-PyMySQLstate: present- name: enable and start mariadbservice:name: mariadbenabled: yesstate: started# 设置 root 密码- name: set password for rootshell: mysqladmin password {{ password }}# 删除匿名用户 anonymous@完整主机名- name: delete user anonymous@{{ ansible_fqdn }}mysql_user:login_host: localhostlogin_port: 3306login_user: rootlogin_password: "{{ password }}"name: ""host: "{{ ansible_fqdn }}"state: absent# 删除匿名用户 anonymous@localhost- name: delete user anonymous@localhostmysql_user:login_host: localhostlogin_port: 3306login_user: rootlogin_password: "{{ password }}"name: ""host: "localhost"state: absent#删除测试数据库- name: delete database testmysql_db:login_host: localhostlogin_port: 3306login_user: rootlogin_password: "{{ password }}"name: teststate: absent# 创建新用户- name: create user {{ user }}mysql_user:login_host: localhostlogin_port: 3306login_user: rootlogin_password: "{{ password }}"name: "{{ user }}"password: "{{ password }}"host: "{{ host }}"priv: "{{ priv }}"state: present# 创建新库- name: create database db_namemysql_db:login_host: localhostlogin_port: 3306login_user: rootlogin_password: "{{ password }}"name: "{{ db_name }}"state: present
[wsh@controller ansible ✔]$ mkdir vault
[wsh@controller ansible ✔]$ ansible-vault create vault/mysql.yml
New Vault password: 
Confirm New Vault password:
user: wsh
password: wsh
host: '%'
priv: '*.*:ALL'# 如果 ansible.cfg 中未配置vault_password_file
# 可使用--ask-vault-pass选项以交互方式提供vault密码
[wsh@controller ansible ✔]$ ansible-playbook playbook.yml --ask-vault-pass
Vault password:

验证:

[wsh@node1 ~ ✔]$ mysql -u wsh -pwsh
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 5.5.68-MariaDB MariaDB ServerCopyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.MariaDB [(none)]> 

变量管理推荐做法

  • 包含敏感变量的文件可通过 ansible-vault 命令进行保护。
  • 敏感变量和所有其他变量保存在相互独立的文件中。
  • 管理组变量和主机变量的首选方式是在项目目录中创建子目录。

可为每个主机组或受管主机使用独立的目录。这些目录可包含多个变量文件,它们都由该主机组或受管主机使用。

示例:

[laoma@controller web]$ tree
.
├── ansible.cfg
├── group_vars
│   ├── all.yaml
│   └── servers.yaml
├── host_vars
│   └── node1
│       ├── vars.yaml
│       └── vaults.yaml
├── inventory
├── playbook.yaml
└── vault└── mysql.yml4 directories, 8 files

node1 的大部分变量可以放在vars.yaml文件中,敏感变量则可单独放在vaults.yaml文件中,并使用ansible-vault加密vault文件,而将vars文件保留为纯文本。

管理 FACTS

FACTS 介绍

FACTS 是 Ansible 在受管主机上自动检测到的变量,默认保存在内容中,只存在于本次playbook执行期间。

FACTS含有主机相关的信息,可以像play中的常规变量一样使用。

受管主机的 facts 包括:

• 主机名称 • 内核版本 • 网络接口 • IP地址 • 操作系统版本 • 各种环境变量
• CPU数量 • 提供的或可用的内存 • 可用磁盘空间

借助 facts,可以方便地检索受管主机的状态,并根据该状态确定要执行的操作。

例如:

  • 可以根据当前内核版本的FACTS运行条件任务,以此来重新启动服务器。
  • 可以根据通过FACTS报告的可用内存来自定义 MySQL 配置文件。
  • 可以根据FACTS的值设置配置文件中使用的 IPv4 地址。

通常,每个play在执行第一个任务之前会先自动收集FACTS。

查看 FACTS 内容

示例1:查看所有变量

---
- name: Dump factshosts: node1tasks:- name: Print all factsdebug:var: ansible_facts

示例2:查看单个变量

---
- hosts: node1tasks:- name: Print Ansible factsdebug: msg: >The default IPv4 address of {{ ansible_fqdn }}is {{ ansible_default_ipv4.address }}

部分 FACTS

FACTVARIABLE
短主机名ansible_facts[‘hostname’]
完全限定的域名ansible_facts[‘fqdn’]
主要IPv4地址(基于路由)ansible_facts[‘default_ipv4’][‘address’]
所有网络接口的名称列表ansible_facts[‘interfaces’]
/dev/vdal磁盘分区的大小ansible_facts[‘devices’][‘vda’][‘partitions’]['vda1][‘size’]
DNS服务器列表ansible_facts[‘dns’][‘nameservers’]
当前运行的内核的版本ansible_facts[‘kernel’]

setup 和 gather_facts 模块

setup 和 gather_facts 模块都可以用来收集facts:

  • gather_facts 模块,只能用来收集facts。

  • setup 模块,除了用来收集facts,还提供额外选项:

    • filter 选项,用于查看特定facts值。

    • gather_subset 选项,用于控制收集facts范围。

[laoma@controller web]$ ansible -m setup node1 -a 'filter=ansible_default_ipv4'
node1 | SUCCESS => {"ansible_facts": {"ansible_default_ipv4": {"address": "172.25.250.10","alias": "enp1s0","broadcast": "172.25.250.255","gateway": "172.25.250.254","interface": "enp1s0","macaddress": "52:54:00:00:fa:0a","mtu": 1500,"netmask": "255.255.255.0","network": "172.25.250.0","type": "ether"},"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false
}

ANSIBLE FACTS 变量注入

在 Ansible2.5之前,FACTS是使用前缀为ansible_字符串的单个变量注入,而不是作为ansible_facts变量的一部分注入。

例如,ansible_facts[‘distribution’] 等同于 ansible_distribution。

许多较旧的playbook仍然使用ansible_前缀的变量,而不是通过ansible_ facts变量引用。

部分facts变量对比:

ANSIBLE_FACTS 格式旧变量格式
ansible_facts[‘hostname’]ansible_hostname
ansible_facts[‘fqdn’]ansible_fqdn
ansible_facts[‘default_ipv4’][‘address’]ansible_default_ipv4[‘address’]
ansible_facts[‘interfaces’]ansible_interfaces
ansible_facts[‘devices’][‘vda’][‘partitions’][‘vda1’][‘size’]ansible_devices[‘vda’][‘partitions’][‘vda1’][‘size’]
ansible_facts[‘dns’][‘nameservers’]ansible_dns[‘nameservers’]
ansible_facts[‘kernel’]ansible_kernel

目前,Ansible同时识别新的FACTS命名系统(使用ansible_facts)和旧的2.5前 “作为单独变量注入的FACTS”命名系统。

Ansible配置置文件[default] 块中inject_facts_as_vars参数设置为false, 可关闭旧命名系统。默认设置目前为true。

如果设置为false,则只能使用新的ansible_facts.* 命名规则。在这种情况下,尝试通过旧命名空间引用FACTS将导致变量未定义错误。

inject_facts_as_vars的默认值在Ansible的未来版本中可能会更改为false。

关闭 FACTS 收集

关闭 FACTS 收集部分原因:

  • 不使用任何FACTS
  • 希望加快play速度或减小play在受管主机上造成的负载
  • 受管主机因为某种原因而无法运行setup模块
  • 需要安装一些必备软件后再收集FACTS

Ansible配置文件设置

[defaults]
gathering = explicit

play中设置

---
- name: Fact dumphosts: node1gather_facts: no

即使关闭以后,也可以随时使用setup模块收集facts。

http://www.xdnf.cn/news/18246.html

相关文章:

  • 【撸靶笔记】第二关:GET -Error based -Intiger based
  • 【LeetCode】单链表经典算法:移除元素,反转链表,约瑟夫环问题,找中间节点,分割链表
  • 计算机网络 TCP三次握手、四次挥手超详细流程【报文交换、状态变化】
  • nn.Module模块介绍
  • USB 2.0声卡
  • 考研复习-操作系统-第一章-计算机系统概述
  • k8s-单主机Master集群部署+单个pod部署lnmp论坛服务(小白的“升级打怪”成长之路)
  • 什么是GD库?PHP中7大类64个GD库函数用法详解
  • 【撸靶笔记】第五关:GET - Double Injection - Single Quotes - String
  • Qt——主窗口 mainWindow
  • GaussDB常用术语缩写及释义
  • 【Golang】:错误处理
  • AI Search进化论:从RAG到DeepSearch的智能体演变全过程
  • 第12章《学以致用》—PowerShell 自学闭环与实战笔记
  • 第七十七章:多模态推理与生成——开启AI“从无到有”的时代!
  • 计算机程序编程软件开发设计之node..js语言开发的基于Vue框架的选课管理系统的设计与实现、基于express框架的在线选课系统的设计与实现
  • Jenkins - CICD 注入环境变量避免明文密码暴露
  • Python中f - 字符串(f-string)
  • Hadoop入门
  • 前端基础知识版本控制系列 - 05( Git 中 HEAD、工作树和索引之间的区别)
  • 图论水题4
  • 写作路上的迷茫与突破
  • java_spring boot 中使用 log4j2 及 自定义layout设置示例
  • NestJS 手动集成TypeORM
  • 关于第一次接触Linux TCP/IP网络相关项目
  • Docker入门:容器化技术的第一堂课
  • python---装饰器
  • 在线编程题目之小试牛刀
  • [每周一更]-(第155期):Go 1.25 发布:新特性、技术思考与 Go vs Rust 竞争格局分析
  • 回溯剪枝的 “减法艺术”:化解超时危机的 “救命稻草”(一)