Docker와 Ansible을 사용하여 개인 로컬 PyPI 서버를 설정하는 방법입니다.[계속]


고사


이 문서는 How I Setup A Private PyPI Server Using Docker And Ansible에서 계속됩니다.
본고에서 저는 DockerAnsible을 사용하여 개인 로컬 PyPI 서버를 설정하는 방법을 상세하게 소개하고자 합니다.

TL;박사 01 명


devpi 컨테이너에서 실행되는 Docker 서버를 단일 명령으로 배포/제거합니다.

어떻게


최초의 research 이후에 배치가 확실하고 PyPI 저장소는 하나의 명령을 통해 임시로 철거하고 다시 만들 수 있기를 희망합니다.우리의 예에서 간단한 make pypiAnsible playbook을 통해 PyPI 서버를 배치한다.
docs:

Ansible Playbooks offer a repeatable, re-usable, simple configuration management and multi-machine deployment system, one that is well suited to deploying complex applications. If you need to execute a task with Ansible more than once, write a playbook and put it under source control. Then you can use the playbook to push out new configuration or confirm the configuration of remote systems.


기본 Ansible 시나리오:
  • 인벤토리
  • 에서 수행할 시스템 선택
  • 은 일반적으로 SSH
  • 을 통해 이러한 시스템(또는 네트워크 장치 또는 기타 관리 노드)에 연결됩니다.
  • 원격 시스템에 하나 이상의 모듈을 복제한 후
  • 실행
    Ansible here에 대한 자세한 내용을 보실 수 있습니다.

    누비다


    설치는 ContainerizationAutomation으로 두 부분으로 나뉜다.
    이 글은 주로 자동화를 소개한다.here으로 컨테이너 운송.

    컨테이너화


    나는 댓글이 너무 길기를 원하지 않는다.
    우편번호: here

    자동화


    Ansible을 자동화하는 이유는 The How을 참조하십시오.

    선결 조건


    Ansible이 이미 설치되어 구성된 경우 이 단계를 건너뛸 수 있습니다. 그렇지 않으면 설치 방법을 검색할 수 있습니다.
    python3 -m pip install ansible paramiko
    
    의존 플러그인도 설치되었는지 확인하십시오.
    ansible-galaxy collection install \
        ansible.posix \
        community.docker
    

    디렉토리 구조


    이 절에서, 나는 pypi_server 디렉터리에 있는 모든 파일을 훑어볼 것이다. 그중에 설정이 포함되어 있다.
    ├── ansible.cfg
    ├── ansible-requirements-freeze.txt
    ├── host_inventory
    ├── Makefile
    ├── README.md
    ├── roles
    │   └── pypi_server
    │      ├── defaults
    │      │    └── main.yml
    │      ├── files
    │      │    └── simple_test-1.0.zip
    │      ├── tasks
    │      │    └── main.yml
    │      └── templates
    │           └── nginx-pypi.conf.j2
    └── up_pypi.yml
    

    이변 구조
    Ansible의 일부 설정은 구성 파일(Ansible.cfg)에서 조정할 수 있습니다.재고 설정은 대다수 사용자에게 충분하지만, 우리의 예에서, 우리는 일부 설정이 필요하다.다음은 저희 ansible.cfg의 샘플입니다.
    cat >> ansible.cfg << EOF
    [defaults]
    inventory=host_inventory
    
    # https://github.com/ansible/ansible/issues/14426
    transport=paramiko
    
    [ssh_connection]
    pipelining=True
    EOF
    
    소프트웨어 패키지 관리자(예를 들어 apt)에서 Ansible를 설치하면 최신 ansible.cfg 파일이 /etc/ansible에 나타날 것이다.pip 또는 소스 코드에서 Ansible을 설치하는 경우 Ansible의 기본 설정을 덮어쓰려면 이 파일을 만들어야 할 수 있습니다.
    wget https://raw.githubusercontent.com/ansible/ansible/devel/examples/ansible.cfg
    

    기계가 재고에서 명령을 실행하는 것을 선택하십시오
    Ansible은 인벤토리에서 관리할 시스템에 대한 정보를 읽습니다.IP 주소를 임시 명령에 전달할 수 있지만 Ansible의 유연성과 중복성을 충분히 활용하기 위해 목록이 필요합니다.
    cat >> host_inventory << EOF
    vagrant ansible_host=192.168.50.4 ansible_user=root ansible_become=yes
    
    [pypi_server]
    vagrant
    EOF
    
    이 문장에 대해 나는 Vagrant 상자를 사용할 것이다.
    docs:

    Vagrant is a tool for building and managing virtual machine environments in a single workflow. With an easy-to-use workflow and focus on automation, Vagrant lowers development environment setup time, increases production parity, and makes the "works on my machine" excuse a relic of the past.

    Vagrant will isolate dependencies and their configuration within a single disposable, consistent environment, without sacrificing any of the tools you are used to working with (editors, browsers, debuggers, etc.).


    다음은 Vagrantfile입니다. 로컬 개발에 사용할 수 있습니다. vagrant up을 실행하기만 하면 됩니다. 모든 물건을 설치하고 설정하면 일을 할 수 있습니다.
    cat >> Vagrantfile << EOF
    # -*- mode: ruby -*-
    # vi: set ft=ruby :
    # set up the default terminal
    ENV["TERM"]="linux"
    
    Vagrant.configure(2) do |config|
        config.vm.box = "opensuse/Leap-15.2.x86_64"
    
        config.ssh.username = 'root'
        config.ssh.password = 'vagrant'
        config.ssh.insert_key = 'true'
    
        config.vm.network "private_network", ip: "192.168.50.4"
        config.vm.network "forwarded_port", guest: 3141, host: 3141 # devpi Access
    
        # consifure the parameters for VirtualBox provider
        config.vm.provider "virtualbox" do |vb|
            vb.memory = "1024"
            vb.cpus = 1
            vb.customize ["modifyvm", :id, "--ioapic", "on"]
        end
        config.vm.provision "shell", inline: <<-SHELL
          zypper --non-interactive install python3 python3-setuptools python3-pip
          zypper --non-interactive install docker
          systemctl enable docker
          usermod -G docker -a $USER
          systemctl restart docker
        SHELL
    end
    EOF
    
    그리고 다음 명령을 실행합니다. 이 명령은 원격 서버의 권한 수여 키에 SSH 키를 설치할 수 있도록 합니다. 이 명령은 SSH 키 로그인에 도움이 되며, 로그인할 때마다 비밀번호가 필요하지 않기 때문에 비밀번호가 필요하지 않은 자동 로그인 과정을 확보합니다.
    # password: vagrant
    ssh-copy-id [email protected]
    
    vagrant box가 시작되면 ping 모듈 ping 인벤토리의 모든 노드를 사용합니다.
    ansible all -m ping
    
    다음 그림과 같이 목록에 있는 각 호스트의 출력을 보아야 합니다.


    허용 가능한 역할
    docs:

    Roles let you automatically load related vars, files, tasks, handlers, and other Ansible artefacts based on a known file structure. After you group your content into roles, you can easily reuse them and share them with other users.

    An Ansible role has a defined directory structure with eight main standard directories. You must include at least one of these directories in each role. You can omit any directories the role does not use.


    Ansible과 함께 ansible-galaxy CLI 도구를 사용하면 init 명령을 사용하여 역할을 생성할 수 있습니다.예를 들어, 다음 항목은 현재 작업 디렉토리에 pypi_server이라는 역할 디렉토리 구조를 생성합니다.
    ansible-galaxy init pypi_server
    
    윗글 Directory Structure 참조.
    기본적으로 Ansible은 역할의 각 디렉토리에서 main.yml 파일을 찾아 관련 내용을 찾습니다.
  • defaults/main.yml: 캐릭터의 기본 변수입니다.
  • files/main.yml: 캐릭터 배치 파일.
  • handlers/main.yml: 처리 프로그램으로 이 캐릭터 내부 또는 외부에서 사용할 수 있습니다.
  • meta/main.yml: 캐릭터의 메타데이터, 캐릭터 의존항을 포함한다.
  • tasks/main.yml: 캐릭터가 수행하는 작업의 메인 목록입니다.
  • templates/main.yml: 캐릭터 배치의 템플릿.
  • vars/main.yml: 캐릭터의 기타 변수.
  • 극본


    PyPI 서버를 배포하는 Playbook을 아래에 정의했습니다.
    cat >> up_pypi.yml <<EOF
    --------
    - name: configure and deploy a PyPI server
      hosts: pypi_server
      roles:
        - role: pypi_server
          vars:
            fqdn: # Fully qualified domain name
            fqdn_port: 80
            host_ip: "{{ hostvars[groups['pypi_server'][0]].ansible_default_ipv4.address }}"
            nginx_reverse_proxy: reverse_proxy
    EOF
    
    이 게시물은 nginx_reverse_proxy을 만드는 방법과 관련이 있습니다.
  • NGINX Reverse Proxy
  • How to Set Up an Nginx Reverse Proxy
  • How to setup Nginx Reverse Proxy
  • How to Deploy NGINX Reverse Proxy on Docker
  • Setting up a Reverse-Proxy with Nginx and docker-compose
  • 연주하다

    files/ :
    이것은 간단한 PyPI 업로드 테스트 프로그램입니다.simple_test에서 다운로드한 https://pypi.org 패키지를 수정했습니다.
    다운로드: simple_test-1.0.zipdefaults/main.yml :
    이것은 캐릭터의 기본 변수입니다. 모든 사용 가능한 변수 중에서 우선순위가 가장 낮고 재고 변수를 포함하여 다른 변수에 쉽게 덮어쓸 수 있습니다.tasks에서 기본 변수로 사용
    cat >> defaults/main.yml << EOF
    --------
    container_name : pypi_server
    base_image : << Your Docker Registry>>/pypi_server:latest
    
    devpi_client_ver: '5.2.2'
    
    devpi_port: 3141
    devpi_user: devpi
    devpi_group: devpi
    
    devpi_folder_home: ./.devpi
    devpi_nginx: /var/data/nginx
    
    EOF
    
    tasks/main.yml :
    main.yml 파일에서 우리는 캐릭터가 순서대로 수행하는 임무 목록이 하나 있다(그 중 어떤 임무가 실패하면 전체 대본이 실패한다).
  • 에는 aptpython 패키지가 설치되어 있습니다.
  • apt 캐시를 업데이트하고 python3-pip을 설치합니다.
  • 설치 ansible-docker 의존항.
  • 에서 devpi을 시작하고 nginx 라우팅을 구성합니다.
  • devpi 컨테이너에 docker 서버를 부팅합니다.
  • 을 30초 동안 일시 중지하여 서버가 시작되었는지 확인합니다.
  • 컨테이너의 개봉 여부를 확인합니다.
  • PyPI 사용자 및 색인을 만듭니다.
  • 템플릿 docker 리버스 에이전트 구성
  • nginx 리버스 에이전트가 시작되었는지 확인합니다.
  • 리버스 에이전트를 다시 로드합니다.
  • PY1041710 서버가 실행 중인지 확인하십시오.
  • 은 가상 환경에 nginx 의존 항목을 로컬로 설치합니다.
  • 인덱스가 위로 향하는지 확인하고 nginx 루트를 확인하세요!
  • 은 PyPI 사용자로 python에 로그인합니다.
  • devpi 패키지의 경로를 찾습니다.
  • nginx팩을 devpi에 업로드합니다.
  • 에서 패키지가 업로드되었는지 확인합니다.
  • 은 PyPI 서버에서 simple-test 패키지를 설치합니다.
  • 쓰레기 정리.
  • 주의: 이 작업들은 원격 서버에서 실행되며, 이 예에서는 유랑자 상자입니다.
    다음은 simple-test으로 PyPI 서버의 설정, 배치와 테스트(vagrant 상자에서)를 상세하게 소개합니다.
    cat >> tasks/main.yml << EOF
    --------
    - name: Install apt and python packages
      block:
      - name: update apt-cache and install python3-pip.
        apt:
          name: python3-pip
          state: latest
          update_cache: yes
    
      - name: install ansible-docker dependencies.
        pip:
          name: docker-py
          state: present
    
      become: yes
      tags: [devpi, packages]
    
    - name: start devpi and configure Nginx routings
      block:
      - name: start devpi server on the docker container.
        community.docker.docker_container:
          name: "{{ container_name }}"
          image: "{{ base_image }}"
          volumes:
          - "{{ devpi_folder_home }}:/root/.devpi"
          ports:
          - "{{ devpi_port }}:{{ devpi_port }}"
          restart_policy: on-failure
          restart_retries: 10
          state: started
    
      - name: pause for 30 seconds to ensure server is up.
        pause:
          seconds: 30
    
      - name: "confirm if {{ container_name }} docker is up"
        community.docker.docker_container:
          name: "{{ container_name }}"
          image: "{{ base_image }}"
          state: present
    
      - name: create pypi user and an index.
        shell: "docker exec -ti {{ container_name }} /bin/bash -c '/data/create_pypi_index.sh'"
        register: command_output
        failed_when: "'Error' in command_output.stderr"
    
      - name: template nginx reverse proxy config
        template:
          src: "nginx-pypi.conf.j2"
          dest: "{{ devpi_nginx }}/{{ fqdn }}.conf"
    
      - name: "check if {{ nginx_reverse_proxy }} is up"
        community.docker.docker_container_info:
          name: "{{ nginx_reverse_proxy }}"
        register: result
    
      - name: "reload {{ nginx_reverse_proxy }}: nginx service"
        shell: "docker exec -ti {{ nginx_reverse_proxy }} bash -c 'service nginx reload'"
        when: result.exists
    
      - name: pause for 30 seconds to ensure nginx is reloaded.
        pause:
          seconds: 30
    
      tags: [docker, nginx]
    
    - name: check if pypi server is running!
      delegate_to: localhost
      connection: local
      block:
      - name: install python dependencies locally in a virtual environment
        pip:
          name: devpi-client
          version: "{{ devpi_client_ver }}"
          virtualenv: /tmp/venv
          virtualenv_python: python3
          state: present
    
      - name: "check if devpi's index is up and confirm nginx routing!"
        shell: "/tmp/venv/bin/devpi use http://{{ fqdn }}/pypi/trusty"
    
      - name: login to devpi as pypi user
        shell: "/tmp/venv/bin/devpi login pypi --password="
    
      - name: find path to simple-test package
        find:
          paths: "."
          patterns: '*.zip'
          recurse: yes
        register: output
    
      - name: upload simple-test package to devpi
        shell: "/tmp/venv/bin/devpi upload {{ output.files[0]['path'] }}"
    
      - name: check if package was uploaded
        shell: "/tmp/venv/bin/devpi test simple-test"
    
      - name: install python package from pypi server
        pip:
          name: pip
          virtualenv: /tmp/venv
          extra_args: >
            --upgrade
            -i  http://{{ fqdn }}/pypi/trusty
            --trusted-host {{ fqdn }}
    
      - name: garbage cleaning
        file:
          path: "/tmp/venv"
          state: absent
    
      tags: [tests]
    EOF
    
    devpi :
    Ansible은 Jinja2 templating을 사용하여 동적 표현식과 변수 액세스를 활성화합니다.
    다음은 로컬 호스트 라우팅에서 전용 Nginx에 이르는 FQDN (Fully qualified domain name
    )
    템플릿 구성 파일입니다.
    cat >> nginx-pypi.conf.j2 <<EOF
    server {
        server_name {{ fqdn }};
        listen 80;
    
        gzip             on;
        gzip_min_length  2000;
        gzip_proxied     any;
        gzip_types       application/json;
    
        proxy_read_timeout 60s;
        client_max_body_size 70M;
    
        # set to where your devpi-server state is on the filesystem
        root {{ devpi_folder_home }};
    
        # try serving static files directly
        location ~ /\+f/ {
            # workaround to pass non-GET/HEAD requests through to the named location below
            error_page 418 = @proxy_to_app;
            if ($request_method !~ (GET)|(HEAD)) {
                return 418;
            }
    
            expires max;
            try_files /+files$uri @proxy_to_app;
        }
        # try serving docs directly
        location ~ /\+doc/ {
            # if the --documentation-path option of devpi-web is used,
            # then the root must be set accordingly here
            root {{ devpi_folder_home }};
            try_files $uri @proxy_to_app;
        }
        location / {
            # workaround to pass all requests to / through to the named location below
            error_page 418 = @proxy_to_app;
            return 418;
        }
        location @proxy_to_app {
            proxy_pass http://{{ host_ip }}:{{ devpi_port }};
            proxy_set_header X-outside-url $scheme://$http_host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    EOF
    

    Makefile


    다음은 Makefile의 한 부분입니다. 의존항을 설치하고 PyPI 서버를 설정하는 데 더욱 쉽습니다.즉, 종속 항목을 설치하고 PyPI 서버를 시작하려면 전체 python 또는 main.yml 명령을 입력하지 않고 다음을 수행할 수 있습니다.
    make install_pkgs pypi
    
    너도 내가 정성들여 디자인한 Makefile here을 볼 수 있다.
    cat >> Makefile << EOF 
    .DEFAULT_GOAL := help
    
    define PRINT_HELP_PYSCRIPT
    import re, sys
    print("Please use `make <target>` where <target> is one of\n")
    for line in sys.stdin:
        match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line)
        if match:
            target, help = match.groups()
            if not target.startswith('--'):
                print(f"{target:20} - {help}")
    endef
    
    export PRINT_HELP_PYSCRIPT
    
    help:
        python3 -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
    
    install_pkgs:  ## Install Ansible dependencies locally.
        python3 -m pip install -r ansible-requirements-freeze.txt
    
    lint: *.yml  ## Lint all yaml files
        echo $^ | xargs ansible-playbook -i host_inventory --syntax-check
    
    pypi: ## Setup and start PyPI server
        ansible-playbook -i host_inventory -Kk up_pypi.yml
    EOF
    

    최종 테스트

    templates/ 버전을 확인하기 위해 다음을 실행/실행했습니다.
  • 컨테이너 중지 pip 서버
  • 에서 ansible-playbook 제거
  • 에서 Docker 이미지를 생성하여 로컬 Docker 레지스트리로 전송하는 CI 작업이 실행되었습니다.
  • 은 ansible 전용 서버의 현재 작업 디렉터리(ansible 역할)에서 pypi_server을 실행하여 pypi_server 용기를 시작합니다.
  • pypi_server FQDN 켜짐(pypi_server)
  • 은 가상 환경에 무작위 Python 패키지를 설치한 후 휠을 재구성한 후 make pypi으로 밀어냄
  • 결론


    축하해!!!
    FQDN에 액세스하면 색인이 나열된 devpi 홈 페이지를 볼 수 있습니다.

    모든 것이 정확하게 설정되었다고 가정하다.현재, 설정 관리 아래의 Docker 용기에서 로컬/개인 PyPI 서버를 실행하여, 확정적인 구축과 단일 명령으로 철거하거나 시작할 수 있도록 합니다.
    이것은 나에게 아주 좋은 Ansible와 Nginx 학습 곡선이다. 만약 네가 이미 본문의 끝에 이르렀다면.감사합니다!

    참고 문헌

  • Ansible
  • Intro to playbooks
  • Ansible-devpi
  • Nginx
  • 좋은 웹페이지 즐겨찾기