У Android-команды поломался «деплой» через отправку письма с вложением на Gmail (было сделано ещё до меня), и появилась необходимость быстренько «накостылить» репозиторий.
Планировался он давно, но сейчас будет без всякой автоматизации — просто руками поднять, запустить, что бы они могли деплоить.
Использовать будем free-версию Sonatype Nexus , запускать будем на AWS EC2 , из Docker-образа, бекенд будет в AWS S3 корзине.
Запуск Nexus из Docker
Обновляем систему, устанавливаем NGINX, и пока его стопаем:
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
Устанавливаем Docker:
root@ip-172-31-36-107:/home/admin# curl https://get.docker.com/ | bash
И Docker Compose:
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
Делаем тестовый запуск Nexus:
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
...
Проверяем:
Дефолтный логин-пасс — admin:admin123 , можно зайти, посмотреть как всё выглядит под админом:
Добавление репозитория
Добавляем репозиторий — переходим в Repositories :
Кликаем Create repository , выбираем raw(hosted) (см. описание тут>>> ):
API
Попробуем в него загрузить файл.
Деплой из Jenkins будет curl
-ом через Nexus API, для работы с которым есть отличный конструктор запросов:
Выбираем POST /v1/components , жмём Try it out , указываем что и куда будем загружать, нам тут интересен Request URL :
Загружаем файл curl
-ом:
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
Проверяем:
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
}
ОК, тут всё работает.
Файл настроек
Дефолтный файл настроек — nexus.properties
.
Находим имя контейнера:
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
Подключаемся к нему:
root@ip-172-31-36-107:/home/admin# docker exec -ti laughing_babbage bash
Находим файл:
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
И проверяем содержимое:
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
И все данные Nexus-а — потом будем подключать с внешнего каталога в контейнер:
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
Авторизация
Сейчас Nexus разрешает загрузку из репозитория без авторизации — это плохо.
Отключаем в Anonymous :
Проверяем:
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.
S3 backend и Blob store
Последний штрих — проверить работу с AWS S3, который у нас будет использоваться для хранения данных.
Создаём корзину:
Создаём политику для доступа к корзине:
Добавляем IAM пользователя:
Подключаем ему политику:
Возвращаемся к Nexus, настраиваем новый Blob Store :
Выбираем тип S3 , заполняем поля:
Добавление репозитория
Добавляем репозиторий, тот же raw (hosted) , но теперь выбираем новое хранилище:
Пробуем запостить туда файл:
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
Проверяем корзину:
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
Отлично — Nexus сохранил данные в корзину, всё работает.
Compose и persistent-data
Теперь добавим Compose-файл, который будет запускать Nexus и подключить каталог с данными с хоста.
Создаём новый EBS, подключаем его к EC2:
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
Создаём на нём раздел:
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.
Создаём файловую систему на /dev/xvdf1
:
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
Получаем ID раздела:
root@ip-172-31-36-107:/home/admin# blkid /dev/xvdf1
/dev/xvdf1: UUID="a0250cb9-b9c7-4ca7-b144-83d8c00a00a5" TYPE="ext4" PARTUUID="7d091a57-01"
Добавляем его в /etc/fstab
:
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
Создаём каталог:
root@ip-172-31-36-107:/home/admin# mkdir /nexus-data
Создаём пользователя:
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
Пользователь в образе Nexus работает UID 200, меняем его для нашего пользователя:
root@ip-172-31-36-107:/home/admin# usermod -u 200 nexus
И для группы:
root@ip-172-31-36-107:/home/admin# groupmod -g 200 nexus
Обновляем владельца каталога:
root@ip-172-31-36-107:/home/admin# chown -R nexus:nexus /nexus-data/
Создаём каталог для Compose-файла:
root@ip-172-31-36-107:/home/admin# mkdir /opt/nexus
Создаём 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"
Проверяем — запускаем его:
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
...
Создаём 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
Проверяем:
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
Логи:
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
...
Добавляем в автозапуск:
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.
Повторяем настройку Blob store, создаём репозиторий, загружаем файл:
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
Проверяем:
NGINX и SSL
Устанавливаем Let’s Encrypt:
git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
Добавляем файл /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:
root@ip-172-31-36-107:/etc/nginx# systemctl restart nexus
Проверяем и перезапускаем NGINX:
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
Получаем сертификат:
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
Обновляем 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;
}
}
Проверяем конфиги, перезапускаем:
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
И проверяем работу Nexus:
В целом на этом всё.
Уже когда буду делать автоматизацию — EBS будет бекапиться по расписанию, плюс у самого Nexus есть возможность бекепа-восстановления .