Nexus 3 OSS เป็น Open Source artifact repository manager ที่จัดการหลาย formats ได้ เช่น container images, Python PIP, Java jar และอื่นๆ อีกมาก
ทำไมต้องมี on-premise artifact manager? มีหลายเหตุผล:
- ใช้ private infrastructure ของคุณเอง: คุณอาจมี proprietary code ที่ต้องปกป้อง
- ดาวน์โหลด artifacts เร็วขึ้น: ถ้าคุณดาวน์โหลด artifacts เดียวกันซ้ำๆ ผ่านอินเทอร์เน็ต คุณสามารถ cache ไว้ที่ central location เพื่อประโยชน์ของ multiple users across multiple servers
- ควบคุม artifacts ที่เข้าสู่ build chain: Centralize artifact location ให้แน่ใจว่าถูก approve สำหรับการใช้งาน และ confirm ว่าไม่มี malicious code
- แยกผู้ที่เข้าถึง artifacts ได้: คุณอาจมี requirements ที่เข้มงวดกว่าเกี่ยวกับใครเข้าถึง artifacts บางอย่างในองค์กรได้
ในบทความนี้ ผมจะแสดงวิธี download, install และ configure OSS version ของ Nexus 3 โดยใช้ Ansible playbook
Nexus 3 จะรันบน Orange PI 5 คอมพิวเตอร์ที่มี RAM 8 GB แต provisioning นี้ทำได้บน machine ใดๆ ที่มี minimum requirements ส่วนหนึ่งของ setup จะเป็นการตั้ง proxy สำหรับ PyPI.org สำหรับ machines ที่อยู่ใน inventory file ของผม
สิ่งที่คุณต้องการเพื่อรันโค้ดจาก tutorial นี้
- Internet connection เพื่อดาวน์โหลด source code สำหรับ Ansible playbook, Nexus และ PIP modules
- Linux machines 2 เครื่องขึ้นไป (ผมใช้ Debian, Armbian และ Fedora IOT) RAM อย่างน้อย 8 GB cluster ของผมมี mix ของ Raspberry PI 4 และ OrangePI 5
- Ansible controller จะรันบน Fedora machine แต server ใดก็ได้สามารถเป็น controller ติดตั้ง Ansible ทำได้ง่าย
Playbook Organization
ผมแบ่ง tasks เป็น groups และ playbook ที่ได้เป็นแบบนี้:
[josevnz@dmaf5 Nexus3OnOrangePI]$ tree -N ansible/
ansible/
├── inventories
│ └── home
│ └── hosts.yaml
├── roles
│ ├── clients
│ │ ├── tasks
│ │ │ └── main.yaml
│ │ └── templates
│ │ └── pip.conf.j2
│ └── nexus
│ ├── files
│ │ └── swagger.json
│ ├── tasks
│ │ ├── download.yaml
│ │ ├── install.yaml
│ │ ├── main.yaml
│ │ ├── post_install.yaml
│ │ ├── pre_install.yaml
│ │ ├── repositories.yaml
│ │ ├── third_party.yaml
│ │ └── user.yaml
│ └── templates
│ ├── logrotate.nexus3.j2
│ ├── nexus3.service.j2
│ ├── nexus.rc.j2
│ └── nexus.vmoptions.j2
├── site.yaml
├── vars
│ ├── clients.yaml
│ └── nexus.yaml
└── vault
├── nexus_password.enc
└── README.md
13 directories, 21 files
มา解釋นิดหน่อย:
- มี 2 roles: 'nexus' และ 'clients' nexus role ใช้ setup artifact management software ส่วน client role ตั้งค่า pip settings บนทุก machine
- Vars มี variables ที่ใช้ในแต่ละ role แยกไฟล์เพื่อให้การใช้งานชัดเจน
- เรามี passwords และจัดการด้วย Ansible vault feature
- ไฟล์ 'site.yaml' Orchestrates role execution:
- hosts: all
tags: clients
vars_files:
- vars/clients.yaml
roles:
- clients
- hosts: nexus_server
tags: nexus
become_user: root
become: true
vars_files:
- vars/nexus.yaml
roles:
- nexus
มา看看 universe ที่ playbook จะ execute
The Host Inventory
ในกรณีของผม ค่อนข้าง simple — มี 2 main groups: 'clients' และ machine ที่ Nexus 3 server จะรัน:
all:
children:
nexus_server:
hosts:
orangepi5.home:
home_lab:
hosts:
dmaf5.home:
raspberrypi.home:
orangepi5.home:
ขั้นตอนสำคัญต่อไปคือ download และ configure Nexus 3
วิธีติดตั้ง Nexus 3
ไฟล์ main.yaml อธิบายลำดับและวัตถุประสงค์ของแต่ละ installation task สำหรับ Nexus role:
# Tasks listed here are related to the remote Nexus 3 server
# Included tasks are called in order
---
- include_tasks: third_party.yaml
- include_tasks: pre_install.yaml
- include_tasks: download.yaml
- include_tasks: install.yaml
- include_tasks: post_install.yaml
- include_tasks: user.yaml
- include_tasks: repositories.yaml
มา看看 "core tasks" ก่อน:
- third_party.yaml: ตรงนี้ติดตั้ง OpenJDK8 (Nexus 3 เขียนด้วย Java) และ logrotate ดูแล stale logs
- pre_install.yaml: เกิดอะไรเยอะที่นี่ เช่น สร้าง required directories สำหรับ nexus, dedicated non-privileged user ที่จะ run process
- download.yaml: ตามชื่อ ดาวน์โหลด fresh version ของ Nexus 3 OSS software และ verify checksum ไม่อยากติดตั้ง malware จากอินเทอร์เน็ต
จากนั้น tasks ที่อยู่ใน "customized installation group":
- install.yaml: Unpack software, เตรียม systemd unit ให้ start อัตโนมัติ, setup JVM settings สำหรับ Nexus, และ deploy logrotate configuration
- post_install.yaml: exciting stuff เกิดที่นี่ — software ติดตั้งแล้ว รันครั้งแรก เรายังเปลี่ยน default password ด้วย REST API เพื่อไปขั้น customization
- user.yaml: ที่นี่เราเตรียมให้ end users เข้าถึง services ที่ Nexus เสนอได้อย่างเหมาะสม ทำด้วย combination ของ REST-API และ Ansible client code:
# https://help.sonatype.com/repomanager3/installation-and-upgrades/post-install-checklist
# https://help.sonatype.com/repomanager3/integrations/rest-and-integration-api
---
- name: Enable anonymous user
tags: anonymous
ansible.builtin.uri:
user: ""
password: ""
url: "/v1/security/anonymous"
method: PUT
body_format: raw
status_code: [ 200, 202, 204 ]
headers:
Content-Type: application/json
body: |-
{
"enabled" : true,
"userId" : "anonymous",
"realmName" : "NexusAuthorizingRealm"
}
force_basic_auth: true
return_content: true
any_errors_fatal: true
- name: Enable Docker security realm
tags: docker_realm
ansible.builtin.uri:
user: ""
password: ""
url: "/v1/security/realms/active"
method: PUT
body_format: raw
status_code: [ 200, 202, 204 ]
headers:
Content-Type: application/json
body: |-
[ "NexusAuthenticatingRealm", "NexusAuthorizingRealm", "DockerToken" ]
force_basic_auth: true
return_content: true
any_errors_fatal: true
Logic ง่ายต่อการติดตาม ด้วยการใช้ 'PUT' http method คุณบอกได้ว่านี่คือ modification operation (หมายถึง existing roles และ users มีอยู่แล้ว) Error detection ทำโดยการได้ HTTP codes ที่ Nexus ส่งกลับ
ขั้นตอนต่อไปคือเตรียม local PyPi proxy นี่คือ multistep task และจะอธิบายรายละเอียดต่อไป
วิธีตั้งค่า PyPI Proxy บน Nexus 3
ไฟล์สุดท้ายใน Nexus 3 role คือ 'repositories.yaml' ที่นี่เราผ่านขั้นตอนต่อไปนี้:
- Check ถ้า proxy setup แล้ว (GET หรือ read-only operation)
- ถ้ายังไม่มี สร้างใหม่ (POST method พร้อม JSON payload รายละเอียดสร้าง repository ใหม่ทั้งหมด)
สังเกตว่า playbook นี้ไม่เสนอ option update repository settings ทำได้กับ REST API แตผมทิ้งเป็น exercise ให้ reader
Tasks สำหรับเตรียม PyPi proxy ด้านล่าง:
# Create proxy for repositories
# https://help.sonatype.com/repomanager3/integrations/rest-and-integration-api
# PyPi: https://pip.pypa.io/en/stable/user_guide/
---
- name: Check if the PyPi proxy exists
tags: pypi_proxy_exists
ansible.builtin.uri:
user: ""
password: ""
url: "/v1/repositories/pypi/proxy/python_proxy"
method: GET
body_format: raw
status_code: [ 200, 202, 204, 404 ]
headers:
Content-Type: application/json
force_basic_auth: true
return_content: true
any_errors_fatal: true
register: python_local
- name: Create PyPI proxy
tags: pypi_proxy_create
ansible.builtin.uri:
user: ""
password: ""
url: "/v1/repositories/pypi/proxy"
method: POST
body_format: raw
status_code: [ 201 ]
headers:
Content-Type: application/json
body: |-
{
"name": "python_proxy",
"online": true,
"storage": {
"blobStoreName": "default",
"strictContentTypeValidation": true
},
"proxy": {
"remoteUrl": "https://pypi.org/",
"contentMaxAge": -1,
"metadataMaxAge": 1440
},
"negativeCache": {
"enabled": true,
"timeToLive": 1440
},
"httpClient": {
"blocked": false,
"autoBlock": true,
"connection": {
"retries": 0,
"timeout": 60,
"enableCircularRedirects": false,
"enableCookies": true,
"useTrustStore": false
}
}
}
force_basic_auth: true
return_content: true
any_errors_fatal: true
when: python_local.status == 404
เราเกือบเสร็จ ตอนนี้เราต้องบอก PyPi clients ว่าควรใช้ local Nexus ของเรา แทนที่จะไป PyPi site โดยตรงเพื่อหา Python libraries
วิธีตั้งค่า Clients
Clients role เรียบง่ายกว่ามาก แค่ต้อง deploy template สำหรับ pip.conf พร้อมข้อมูลพอที่จะ force search บน repository ใหม่ของเรา:
# Tasks here are meant to be used on our clients user
---
- name: Create installation directory for pip.conf
tags: pip_basedir
ansible.builtin.file:
state: directory
path: ""
owner: ""
group: ""
mode: "u+rwx,go-rwx"
- name: Copy pip.conf file
tags: pip_copy
ansible.builtin.template:
src: pip.conf.j2
dest: "/pip.conf"
owner: ""
group: ""
mode: u=rxw,g=r,o=r
ไฟล์ที่ได้จะถูก deploy ไปที่ '~/.config/pip/pip.conf' ของทุก machine:
# https://pip.pypa.io/en/stable/topics/configuration/
[global]
timeout = 60
[install]
index = https://orangepi5.home:8081/repository/python_proxy/pypi
index-url = https://orangepi5.home:8081/repository/python_proxy/simple/
trusted-host = orangepi5.home
ไฟล์ด้านบนแสดงตัวอย่าง final version ของไฟล์หลัง deploy บน cluster ของผม (ของคุณจะต่างไปด้วย resolved URL)
ตอนนี้ถึงเวลา run playbook ทั้งหมดและดูว่ามันเป็นยังไง
วิธีรัน Playbook
เพื่อรัน playbook เราส่ง arguments หลายอย่าง:
- ตำแแหน่ง host inventory
- ตำแหน่ง encrypted password file และ master file ที่มี master password เพื่อ unlock เนื้อหา protected file
- และสุดท้าย ตำแหน่ง main playbook file
cd ansible
ansible-playbook --inventory inventories --extra-vars @vault/nexus_password.enc --vault-password-file $HOME/vault/ansible_vault_pass site.yaml

วิธีทดสอบ PyPI proxy ใหม่
เพื่อทดสอบ proxy ใหม่ เราจะติดตั้ง Python Rich ด้วย pip และ virtual environment
josevnz@orangepi5:~$ python3 -m venv ~/virtualenv/rich
(rich) josevnz@orangepi5:~$ . ~/virtualenv/rich/bin/activate
(rich) josevnz@orangepi5:~$ pip install rich
Looking in indexes: https://orangepi5.home:8081/repository/python_proxy/simple/
Collecting rich
Downloading https://orangepi5.home:8081/repository/python_proxy/packages/rich/13.3.4/rich-13.3.4-py3-none-any.whl (238 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 238.7/238.7 KB 14.8 MB/s eta 0:00:00
Collecting pygments<3.0.0,>=2.13.0
Downloading https://orangepi5.home:8081/repository/python_proxy/packages/pygments/2.15.0/Pygments-2.15.0-py3-none-any.whl (1.1 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.1/1.1 MB 23.8 MB/s eta 0:00:00
Collecting markdown-it-py<3.0.0,>=2.2.0
Downloading https://orangepi5.home:8081/repository/python_proxy/packages/markdown-it-py/2.2.0/markdown_it_py-2.2.0-py3-none-any.whl (84 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 84.5/84.5 KB 6.9 MB/s eta 0:00:00
Collecting mdurl~=0.1
Downloading https://orangepi5.home:8081/repository/python_proxy/packages/mdurl/0.1.2/mdurl-0.1.2-py3-none-any.whl (10.0 kB)
Installing collected packages: pygments, mdurl, markdown-it-py, rich
Successfully installed markdown-it-py-2.2.0 mdurl-0.1.2 pygments-2.15.0 rich-13.3.4
และเราสามารถยืนยันว่า cache ถูกใช้จริง โดยดู artifacts ใหม่ใน repository ใหม่:

เห็น PyPi artifacts
มา看看 demo ของ client ในการติดตั้งอย่างอื่น:

Further Customization ด้วย REST-API
ทุก Nexus installation ให้คุณดาวน์โหลด JSON file ที่ describe API ที่ server รองรับ ตัวอย่างใน server ของผม คุณได้ copy จาก orangepi5.home server:
curl --fail --remote-name https://orangepi5.home:8081/service/rest/swagger.json
นอกจากนี้ UI ให้คุณลอง REST API endpoints อื่นๆ เพื่อ customize installation

REST API testing
สรุป
แนะนำให้ใช้เวลาอ่าน Nexus 3 book เพื่อคุ้นเคยกับ features ที่ tool นี้เสนอ
Community เตรียม Debian และ RPM installers ถ้าคุณต้องการ setup แบบนั้น แทนที่จะใช้ Ansible
Nexus 3 มี configurable settings เยอะมาก เราครอบคลุมแค่ผิวหน้าที่นี่ ขณะเตรียมบทความ ผมเจอ 'ThoTeam Nexus3-oss repository' ที่มี playbook complete และ up-to-date มาก แต complex กว่าที่ผมต้องการสำหรับ home lab
Archiva เป็น Open Source artifact manager อีกตัว หน้าที่ limited แต่ setup ง่ายกว่า
มี post-installation checklist บาง tasks ที่ผมไม่ต้องทำสำหรับ home lab โปรดเช็คให้แน่ใจว่า setup ของคุณ complete
เรียนรู้การเขียนโค้ดฟรี หลักสูตรโอเพนซอร์สของ freeCodeCamp ช่วยให้ผู้คน 40,000+ คนได้งานเป็นนักพัฒนา เริ่มต้นเลย