У Android-команды поломался “деплой” через отправку письма с вложением на Gmail (было сделано ещё до меня), и появилась необходимость быстренько “накостылить” репозиторий.
Планировался он давно, но сейчас будет без всякой автоматизации – просто руками поднять, запустить, что бы они могли деплоить.
Использовать будем free-версию Sonatype Nexus, запускать будем на AWS EC2, из Docker-образа, бекенд будет в AWS S3 корзине.
Содержание
Запуск Nexus из Docker
Обновляем систему, устанавливаем NGINX, и пока его стопаем:
[simterm]
root@ip-172-31-36-107:/home/admin# apt update && apt -y upgrade && apt -y install nginx root@ip-172-31-36-107:/home/admin# service nginx stop
[/simterm]
Устанавливаем Docker:
[simterm]
root@ip-172-31-36-107:/home/admin# curl https://get.docker.com/ | bash
[/simterm]
И Docker Compose:
[simterm]
root@ip-172-31-36-107:/home/admin# curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose root@ip-172-31-36-107:/home/admin# chmod +x /usr/local/bin/docker-compose
[/simterm]
Делаем тестовый запуск Nexus:
[simterm]
root@ip-172-31-36-107:/home/admin# docker run -ti -p 8081:8081 sonatype/nexus3 Unable to find image 'sonatype/nexus3:latest' locally latest: Pulling from sonatype/nexus3 ... 2018-09-27 10:18:09,347+0000 INFO [FelixStartLevel] *SYSTEM org.sonatype.nexus.bootstrap.ConfigurationBuilder - application-host='0.0.0.0' 2018-09-27 10:18:09,348+0000 INFO [FelixStartLevel] *SYSTEM org.sonatype.nexus.bootstrap.ConfigurationBuilder - application-port='8081' ... Started Sonatype Nexus OSS 3.13.0-01 ...
[/simterm]
Проверяем:
Дефолтный логин-пасс – admin:admin123, можно зайти, посмотреть как всё выглядит под админом:
Добавление репозитория
Добавляем репозиторий – переходим в Repositories:
Кликаем Create repository, выбираем raw(hosted) (см. описание тут>>>):
API
Попробуем в него загрузить файл.
Деплой из Jenkins будет curl
-ом через Nexus API, для работы с которым есть отличный конструктор запросов:
Выбираем POST /v1/components, жмём Try it out, указываем что и куда будем загружать, нам тут интересен Request URL:
Загружаем файл curl
-ом:
[simterm]
$ curl -v -u admin:admin123 --upload-file nexus-test.txt http://54.154.29.22:8081/repository/test/nexus-test.txt * Trying 54.154.29.22... * TCP_NODELAY set * Connected to 54.154.29.22 (54.154.29.22) port 8081 (#0) * Server auth using Basic with user 'admin' > PUT /repository/test/nexus-test.txt HTTP/1.1 > Host: 54.154.29.22:8081 > Authorization: Basic YWRtaW46YWRtaW4xMjM= > User-Agent: curl/7.61.1 > Accept: */* > Content-Length: 0 > < HTTP/1.1 201 Created < Date: Thu, 27 Sep 2018 10:53:33 GMT < Server: Nexus/3.13.0-01 (OSS) < X-Content-Type-Options: nosniff < Content-Security-Policy: sandbox allow-forms allow-modals allow-popups allow-presentation allow-scripts allow-top-navigation < Content-Length: 0 < * Connection #0 to host 54.154.29.22 left intact
[/simterm]
Проверяем:
[simterm]
$ curl -X GET "http://54.154.29.22:8081/service/rest/v1/components?repository=test" -H "accept: application/json" { "items" : [ { "id" : "dGVzdDpkMzUxMGI3YWQxOGQ4MmNkZTU2M2ExZWUxYWY5YjAwZA", "repository" : "test", "format" : "raw", "group" : "/", "name" : "nexus-test.txt", "version" : null, "assets" : [ { "downloadUrl" : "http://54.154.29.22:8081/repository/test/nexus-test.txt", "path" : "nexus-test.txt", "id" : "dGVzdDpjNzcwMTY5YzBiMmUzZWQ4ZWYzNTZhYTRlOWIyZmU0Ng", "repository" : "test", "format" : "raw", "checksum" : { "sha1" : "da39a3ee5e6b4b0d3255bfef95601890afd80709", "md5" : "d41d8cd98f00b204e9800998ecf8427e" } } ] } ], "continuationToken" : null }
[/simterm]
ОК, тут всё работает.
Файл настроек
Дефолтный файл настроек – nexus.properties
.
Находим имя контейнера:
[simterm]
# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 17ca436cd5b0 sonatype/nexus3 "sh -c ${SONATYPE_DI…" About an hour ago Up About an hour 0.0.0.0:8081->8081/tcp laughing_babbage
[/simterm]
Подключаемся к нему:
[simterm]
root@ip-172-31-36-107:/home/admin# docker exec -ti laughing_babbage bash
[/simterm]
Находим файл:
[simterm]
bash-4.2$ find / -name nexus.properties -type f find: '/root': Permission denied find: '/var/lib/machines': Permission denied find: '/var/lib/yum/history/2018-08-04/1': Permission denied find: '/var/lib/yum/history/2018-08-04/2': Permission denied find: '/var/cache/ldconfig': Permission denied find: '/proc/tty/driver': Permission denied /nexus-data/etc/nexus.properties
[/simterm]
И проверяем содержимое:
[simterm]
bash-4.2$ cat /nexus-data/etc/nexus.properties # Jetty section # application-port=8081 # application-host=0.0.0.0 # nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jetty-http.xml,${jetty.etc}/jetty-requestlog.xml # nexus-context-path=/${NEXUS_CONTEXT} # Nexus section # nexus-edition=nexus-pro-edition # nexus-features=\ # nexus-pro-feature # nexus.clustered=false
[/simterm]
И все данные Nexus-а – потом будем подключать с внешнего каталога в контейнер:
[simterm]
bash-4.2$ ls -l /nexus-data/ total 68 drwxr-xr-x 3 nexus nexus 4096 Sep 27 10:21 blobs drwxr-xr-x 271 nexus nexus 12288 Sep 27 10:20 cache drwxr-xr-x 9 nexus nexus 4096 Sep 27 10:21 db drwxr-xr-x 3 nexus nexus 4096 Sep 27 10:21 elasticsearch drwxr-xr-x 3 nexus nexus 4096 Sep 27 10:20 etc drwxr-xr-x 2 nexus nexus 4096 Sep 27 10:20 generated-bundles drwxr-xr-x 2 nexus nexus 4096 Sep 27 10:20 instances drwxr-xr-x 3 nexus nexus 4096 Sep 27 10:20 javaprefs drwxr-xr-x 3 nexus nexus 4096 Sep 27 10:20 keystores -rw-r--r-- 1 nexus nexus 14 Sep 27 10:20 lock drwxr-xr-x 2 nexus nexus 4096 Sep 27 10:21 log drwxr-xr-x 2 nexus nexus 4096 Sep 27 10:20 orient -rw-r--r-- 1 nexus nexus 5 Sep 27 10:20 port drwxr-xr-x 2 nexus nexus 4096 Sep 27 10:21 restore-from-backup drwxr-xr-x 7 nexus nexus 4096 Sep 27 10:55 tmp
[/simterm]
Авторизация
Сейчас Nexus разрешает загрузку из репозитория без авторизации – это плохо.
Отключаем в Anonymous:
Проверяем:
[simterm]
$ wget http://54.154.29.22:8081/repository/test/android/1.0/debug-v.2.4.0-1-BetaStage.apk --2018-09-27 14:21:42-- http://54.154.29.22:8081/repository/test/android/1.0/debug-v.2.4.0-1-BetaStage.apk Connecting to 54.154.29.22:8081... connected. HTTP request sent, awaiting response... 401 Unauthorized Username/Password Authentication Failed.
[/simterm]
S3 backend и Blob store
Последний штрих – проверить работу с AWS S3, который у нас будет использоваться для хранения данных.
Создаём корзину:
Создаём политику для доступа к корзине:
Добавляем IAM пользователя:
Подключаем ему политику:
Возвращаемся к Nexus, настраиваем новый Blob Store:
Выбираем тип S3, заполняем поля:
Добавление репозитория
Добавляем репозиторий, тот же raw (hosted), но теперь выбираем новое хранилище:
Пробуем запостить туда файл:
[simterm]
$ curl -v -u admin:admin123 --upload-file debug-v.2.4.0-1-BetaStage.apk http://54.154.29.22:8081/repository/android/debug-v.2.4.0-1-BetaStage.apk * Trying 54.154.29.22... * TCP_NODELAY set * Connected to 54.154.29.22 (54.154.29.22) port 8081 (#0) * Server auth using Basic with user 'admin' > PUT /repository/android/debug-v.2.4.0-1-BetaStage.apk HTTP/1.1 > Host: 54.154.29.22:8081 > Authorization: Basic YWRtaW46YWRtaW4xMjM= > User-Agent: curl/7.61.1 > Accept: */* > Content-Length: 14110446 > Expect: 100-continue > < HTTP/1.1 100 Continue * We are completely uploaded and fine < HTTP/1.1 201 Created < Date: Thu, 27 Sep 2018 11:33:11 GMT < Server: Nexus/3.13.0-01 (OSS) < X-Content-Type-Options: nosniff < Content-Security-Policy: sandbox allow-forms allow-modals allow-popups allow-presentation allow-scripts allow-top-navigation < Content-Length: 0 < * Connection #0 to host 54.154.29.22 left intact
[/simterm]
Проверяем корзину:
[simterm]
$ aws --profile bm-backend s3 ls s3://bttrm-nexus-repo PRE content/ 2018-09-27 14:33:19 61 EC08C25B-21CD7317-B8204C96-6480D680-E92F7389-metrics.properties 2018-09-27 14:31:11 40 metadata.properties
[/simterm]
Отлично – Nexus сохранил данные в корзину, всё работает.
Compose и persistent-data
Теперь добавим Compose-файл, который будет запускать Nexus и подключить каталог с данными с хоста.
Создаём новый EBS, подключаем его к EC2:
[simterm]
root@ip-172-31-36-107:/home/admin# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 8G 0 disk └─xvda1 202:1 0 8G 0 part / xvdf 202:80 0 20G 0 disk
[/simterm]
Создаём на нём раздел:
[simterm]
root@ip-172-31-36-107:/home/admin# fdisk /dev/xvdf Welcome to fdisk (util-linux 2.29.2). Changes will remain in memory only, until you decide to write them. Be careful before using the write command. Device does not contain a recognized partition table. Created a new DOS disklabel with disk identifier 0x7d091a57. Command (m for help): p Disk /dev/xvdf: 20 GiB, 21474836480 bytes, 41943040 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x7d091a57 Command (m for help): n Partition type p primary (0 primary, 0 extended, 4 free) e extended (container for logical partitions) Select (default p): Using default response p. Partition number (1-4, default 1): First sector (2048-41943039, default 2048): Last sector, +sectors or +size{K,M,G,T,P} (2048-41943039, default 41943039): Created a new partition 1 of type 'Linux' and of size 20 GiB. Command (m for help): p Disk /dev/xvdf: 20 GiB, 21474836480 bytes, 41943040 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x7d091a57 Device Boot Start End Sectors Size Id Type /dev/xvdf1 2048 41943039 41940992 20G 83 Linux Command (m for help): w The partition table has been altered. Calling ioctl() to re-read partition table. Syncing disks.
[/simterm]
Создаём файловую систему на /dev/xvdf1
:
[simterm]
root@ip-172-31-36-107:/home/admin# mkfs.ext4 /dev/xvdf1 mke2fs 1.43.4 (31-Jan-2017) Creating filesystem with 5242624 4k blocks and 1310720 inodes Filesystem UUID: a0250cb9-b9c7-4ca7-b144-83d8c00a00a5 Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 4096000 Allocating group tables: done Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: don
[/simterm]
Получаем ID раздела:
[simterm]
root@ip-172-31-36-107:/home/admin# blkid /dev/xvdf1 /dev/xvdf1: UUID="a0250cb9-b9c7-4ca7-b144-83d8c00a00a5" TYPE="ext4" PARTUUID="7d091a57-01"
[/simterm]
Добавляем его в /etc/fstab
:
[simterm]
root@ip-172-31-36-107:/home/admin# cat /etc/fstab # /etc/fstab: static file system information. # # <file sys> <mount point> <type> <options> <dump> <pass> # device during installation: /dev/loop0p1 UUID=b524f8c0-90e7-4fc7-a842-6cb2380086c8 / ext4 rw,discard,errors=remount-ro 0 1 # /nexus-data UUID="a0250cb9-b9c7-4ca7-b144-83d8c00a00a5" /nexus-data ext4 defaults 0 2
[/simterm]
Создаём каталог:
[simterm]
root@ip-172-31-36-107:/home/admin# mkdir /nexus-data
[/simterm]
Создаём пользователя:
[simterm]
root@ip-172-31-36-107:/home/admin# adduser nexus --shell=/bin/false --no-create-home --disabled-password Adding user `nexus' ... Adding new group `nexus' (1001) ... Adding new user `nexus' (1001) with group `nexus' ... Not creating home directory `/home/nexus'. Changing the user information for nexus Enter the new value, or press ENTER for the default Full Name []: Room Number []: Work Phone []: Home Phone []: Other []: Is the information correct? [Y/n] y
[/simterm]
Пользователь в образе Nexus работает UID 200, меняем его для нашего пользователя:
[simterm]
root@ip-172-31-36-107:/home/admin# usermod -u 200 nexus
[/simterm]
И для группы:
[simterm]
root@ip-172-31-36-107:/home/admin# groupmod -g 200 nexus
[/simterm]
Обновляем владельца каталога:
[simterm]
root@ip-172-31-36-107:/home/admin# chown -R nexus:nexus /nexus-data/
[/simterm]
Создаём каталог для Compose-файла:
[simterm]
root@ip-172-31-36-107:/home/admin# mkdir /opt/nexus
[/simterm]
Создаём Compose файл /opt/nexus/nexus-server.yml
:
version: '3' networks: nexus: services: nexus: user: nexus image: sonatype/nexus3 networks: - nexus ports: - '8081:8081' volumes: - /nexus-data:/nexus-data logging: driver: "journald"
Проверяем – запускаем его:
[simterm]
root@ip-172-31-36-107:/opt/nexus# docker-compose -f nexus-server.yml up Starting nexus_nexus_1 ... done Attaching to nexus_nexus_1 ...
[/simterm]
Создаём systemd-unit файл /etc/systemd/system/nexus.service
:
[Unit] Description=Nexus repository service Requires=docker.service After=docker.service [Service] Restart=always WorkingDirectory=/opt/nexus # Compose up ExecStart=/usr/local/bin/docker-compose -f nexus-server.yml up # Compose down, remove containers and volumes ExecStop=/usr/local/bin/docker-compose -f nexus-server.yml down -v [Install] WantedBy=multi-user.target
Проверяем:
[simterm]
root@ip-172-31-36-107:/opt/nexus# systemctl start nexus root@ip-172-31-36-107:/opt/nexus# systemctl status nexus ● nexus.service - Nexus repository service Loaded: loaded (/etc/systemd/system/nexus.service; disabled; vendor preset: enabled) Active: active (running) since Thu 2018-09-27 12:41:12 UTC; 1s ago Main PID: 21589 (docker-compose) Tasks: 4 (limit: 4915) Memory: 46.9M CPU: 473ms CGroup: /system.slice/nexus.service ├─21589 /usr/local/bin/docker-compose -f nexus-server.yml up └─21590 /usr/local/bin/docker-compose -f nexus-server.yml up
[/simterm]
Логи:
[simterm]
root@ip-172-31-36-107:/opt/nexus# journalctl -u nexus -f -- Logs begin at Thu 2018-09-27 09:39:32 UTC. -- Sep 27 12:59:40 ip-172-31-36-107 docker-compose[24884]: nexus_1 | at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) Sep 27 12:59:40 ip-172-31-36-107 docker-compose[24884]: nexus_1 | at java.lang.Thread.run(Thread.java:748) Sep 27 12:59:41 ip-172-31-36-107 docker-compose[24884]: nexus_nexus_1 exited with code 0 Sep 27 12:59:41 ip-172-31-36-107 docker-compose[25183]: [71B blob data] Sep 27 12:59:41 ip-172-31-36-107 docker-compose[25183]: [73B blob data] Sep 27 12:59:41 ip-172-31-36-107 docker-compose[24884]: Gracefully stopping... (press Ctrl+C again to force) Sep 27 12:59:41 ip-172-31-36-107 systemd[1]: Stopped Nexus repository service. Sep 27 12:59:42 ip-172-31-36-107 systemd[1]: Started Nexus repository service. Sep 27 12:59:42 ip-172-31-36-107 docker-compose[25284]: Creating network "nexus_nexus" with the default driver Sep 27 12:59:42 ip-172-31-36-107 docker-compose[25284]: Creating nexus_nexus_1 ... Sep 27 12:59:46 ip-172-31-36-107 docker-compose[25284]: [71B blob data] Sep 27 12:59:46 ip-172-31-36-107 docker-compose[25284]: nexus_1 | 2018-09-27 12:59:46,173+0000 INFO [FelixStartLevel] *SYSTEM org.sonatype.nexus.pax.logging.NexusLogActivator - start ...
[/simterm]
Добавляем в автозапуск:
[simterm]
root@ip-172-31-36-107:/opt/nexus# systemctl enable nexus Created symlink /etc/systemd/system/multi-user.target.wants/nexus.service → /etc/systemd/system/nexus.service.
[/simterm]
Повторяем настройку Blob store, создаём репозиторий, загружаем файл:
[simterm]
$ curl -u admin:admin123 --upload-file debug-v.2.4.0-1-BetaStage.apk http://54.154.29.22:8081/repository/android/debug/debug-v.2.4.0-1-BetaStage.apk
[/simterm]
Проверяем:
NGINX и SSL
Устанавливаем Let’s Encrypt:
[simterm]
# git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
[/simterm]
Добавляем файл /etc/nginx/conf.d/nexus-repo.domain.world.conf
:
upstream nexus { server 127.0.0.1:8081; } server { listen 80; server_name nexus-repo.domain.world; # Lets Encrypt Webroot location ~ /.well-known { root /var/www/html; allow all; } location / { return 301 https://nexus-repo.domain.world$request_uri; } }
Обновляем /opt/nexus/nexus-server.yml
– задаём порт хоста 8081 вместо 80, как было во время тестирования-настройки:
version: '3' networks: nexus: services: nexus: user: nexus image: sonatype/nexus3 networks: - nexus ports: - '8081:8081' volumes: - /nexus-data:/nexus-data logging: driver: "journald"
Перезапускаем Nexus:
[simterm]
root@ip-172-31-36-107:/etc/nginx# systemctl restart nexus
[/simterm]
Проверяем и перезапускаем NGINX:
[simterm]
root@ip-172-31-36-107:/etc/nginx# nginx -t && service nginx restart nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
[/simterm]
Получаем сертификат:
[simterm]
root@ip-172-31-36-107:/etc/nginx# /opt/letsencrypt/letsencrypt-auto certonly -a webroot --webroot-path=/var/www/html -d nexus-repo.domain.world
[/simterm]
Обновляем NGINX – добавляем в /etc/nginx/conf.d/nexus-repo.domain.world.conf
server {}
для SSL:
upstream nexus { server 127.0.0.1:8081; } server { listen 80; server_name nexus-repo.domain.world; # Lets Encrypt Webroot location ~ /.well-known { root /var/www/html; allow all; } location / { return 301 https://nexus-repo.domain.world$request_uri; } } server { listen 443 ssl; server_name nexus-repo.domain.world; root /var/www/html; error_log /var/log/nginx/nexus-repo.domain.world-error.log; access_log /var/log/nginx/nexus-repo.domain.world-access.log; ssl on; ssl_certificate /etc/letsencrypt/live/nexus-repo.domain.world/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/nexus-repo.domain.world/privkey.pem; location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_pass http://nexus; } }
Проверяем конфиги, перезапускаем:
[simterm]
root@ip-172-31-36-107:/etc/nginx# nginx -t && service nginx reload nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
[/simterm]
И проверяем работу Nexus:
В целом на этом всё.
Уже когда буду делать автоматизацию – EBS будет бекапиться по расписанию, плюс у самого Nexus есть возможность бекепа-восстановления.