Задача – развернуть Jenkins и Docker Private registry в Китае, на AWS.
Аналогичные посты – тут>>> и тут>>>, только в этом посте всё собрано в одно целое и упорядочено.
К EC2 будут подключены два EBS – один с данными Jenkins, второй – с данными Docker.
Для Docker registry в качестве хранилища используем AWS S3.
Далее в посте:
- создадим два EBS
- запустим EC2 в Китае
- установим Docker, Docker Compose
- подключим EBS, один в
/jenkins
, второй в/docker
- запустим Jenkins из Docker Compose, к которому будет монтироваться
/jenkins
- создадим S3 корзину для образов Registry
- запустим Docker registry из Docker Compose, к которому будет монтироваться
/docker
Содержание
Подготовка
AWS EC2
Запускаем EC2:
EBS
В том же регионе (cn-north-1b) создаём два EBS по 8 GB:
Подключаем их к EC2:
Логинимся на инстанс, проверяем:
[simterm]
$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 8G 0 disk └─xvda1 202:1 0 8G 0 part / xvdb 202:16 0 8G 0 disk xvdc 202:32 0 8G 0 disk
[/simterm]
Создаём файловые системы на разделах:
[simterm]
# sgdisk -n 1 /dev/xvdb Creating new GPT entries. The operation has completed successfully.
[/simterm]
И для Docker:
[simterm]
# sgdisk -n 1 /dev/xvdc Creating new GPT entries. The operation has completed successfully.
[/simterm]
Проверяем:
[simterm]
# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 8G 0 disk └─xvda1 202:1 0 8G 0 part / xvdb 202:16 0 8G 0 disk └─xvdb1 202:17 0 8G 0 part xvdc 202:32 0 8G 0 disk └─xvdc1 202:33 0 8G 0 part
[/simterm]
Создаём файловые системы:
[simterm]
# mkfs.ext4 /dev/xvdb1 # mkfs.ext4 /dev/xvdc1
[/simterm]
Создаём точки монтирования:
[simterm]
# mkdir /jenkins # mkdir /docker
[/simterm]
Находим UUID разделов:
[simterm]
# blkid /dev/xvdb1 /dev/xvdb1: UUID="4da7fd0f-13a9-434b-8d33-f23776abf5f0" TYPE="ext4" PARTUUID="3a264f88-917e-47a9-a966-4daf100965d7" # blkid /dev/xvdc1 /dev/xvdc1: UUID="0b371143-d16a-40a2-9f35-2151952566b2" TYPE="ext4" PARTUUID="98178e64-4e95-42e9-baac-31700463cf6e"
[/simterm]
Редактируем /etc/fstab
(с nofail
опцией):
LABEL=cloudimg-rootfs / ext4 defaults,discard 0 0 UUID=4da7fd0f-13a9-434b-8d33-f23776abf5f0 /jenkins ext4 defaults,nofail 0 2 UUID=0b371143-d16a-40a2-9f35-2151952566b2 /docker ext4 defaults,nofail 0 2
Монтируем все разделы из /etc/fstab
:
[simterm]
# mount -a
[/simterm]
Проверяем:
[simterm]
# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 8G 0 disk └─xvda1 202:1 0 8G 0 part / xvdb 202:16 0 8G 0 disk └─xvdb1 202:17 0 8G 0 part /jenkins xvdc 202:32 0 8G 0 disk └─xvdc1 202:33 0 8G 0 part /docker
[/simterm]
Docker
Обновляем пакеты:
[simterm]
# apt update && apt upgrade
[/simterm]
Устанавливаем Docker:
[simterm]
# curl https://get.docker.com/ | bash
[/simterm]
Docker Compose:
[simterm]
# curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
[/simterm]
Пока устанавливается Compose – обновляем настройки для Docker – меняем data-root
.
Создаём файл /etc/docker/daemon.json
:
{ "data-root": "/docker/" }
Перезапускаем docker
демон:
[simterm]
# systemctl restart docker.service
[/simterm]
Проверяем содержимое /docker
:
[simterm]
# ls -l /docker/ total 64 drwx------ 2 root root 4096 Nov 22 11:16 builder drwx--x--x 3 root root 4096 Nov 22 11:16 containerd drwx------ 2 root root 4096 Nov 22 11:16 containers ...
[/simterm]
Проверяем статус docker
:
[simterm]
# docker info | grep Root Docker Root Dir: /docker
[/simterm]
Systemd
Что бы Docker демон запускался после того, как будут смонтирован каталог /docker
– обновляем systemd-сервис docker.service
.
Проверяем статус точки монтирования /docker
:
[simterm]
# systemctl status docker.mount ● docker.mount - /docker Loaded: loaded (/proc/self/mountinfo) Active: active (mounted) since Wed 2017-11-22 11:12:43 UTC; 5min ago Where: /docker What: /dev/xvdc1 Tasks: 0 Memory: 0B CPU: 0
[/simterm]
В файле /lib/systemd/system/docker.service
в блоке [Unit]
в конец строки After
добавляем docker.mount
:
[Unit] Description=Docker Application Container Engine Documentation=https://docs.docker.com After=network-online.target docker.socket firewalld.service docker.mount Wants=network-online.target Requires=docker.socket ...
Перезагружаем машину, что бы убедиться, что всё работает:
[simterm]
# reboot
[/simterm]
После рестарта – проверяем ещё раз:
[simterm]
# docker info | grep Root Docker Root Dir: /docker
[/simterm]
Всё гуд.
Jenkins
Создаём Compose файл:
version: '3' services: jenkins: user: root image: "daocloud.io/jenkins" restart: "always" ports: - "80:8080" volumes: - /jenkins/:/jenkins - /var/run/docker.sock:/var/run/docker.sock - /usr/bin/docker:/usr/bin/docker environment: - JENKINS_HOME=/jenkins
Используем daocloud.io., что бы не тянуть из hub.docker.com (China ведь).
Запускаем:
[simterm]
# docker-compose up -d Starting jenkins_jenkins_1 Starting jenkins_jenkins_1 ... done
[/simterm]
Проверяем содержимое /jenkins
:
[simterm]
# ls -l /jenkins/ total 92 -rw-r--r-- 1 root root 1592 Nov 22 11:40 config.xml -rw-r--r-- 1 root root 29 Nov 22 11:40 failed-boot-attempts.txt -rw-r--r-- 1 root root 159 Nov 22 11:40 hudson.model.UpdateCenter.xml -rw------- 1 root root 1712 Nov 22 11:38 identity.key.enc drwxr-xr-x 2 root root 4096 Nov 22 11:38 init.groovy.d ...
[/simterm]
Тут пока всё готово.
Docker Registry
S3 для registry storage
Для registry – нам потребуется S3 корзина для хранения образов и SSL сертификат. Запуск Docker registry описан тут>>>.
Создаём корзину:
[simterm]
$ aws s3api --profile tag-cn create-bucket --bucket tag-ci-registry --region cn-north-1 --create-bucket-configuration LocationConstraint=cn-north-1
[/simterm]
Создаём файл политик доступа:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": "*", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:ListBucketMultipartUploads" ], "Resource": "arn:aws-cn:s3:::tag-ci-registry" }, { "Effect": "Allow", "Principal": "*", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListMultipartUploadParts", "s3:AbortMultipartUpload" ], "Resource": "arn:aws-cn:s3:::tag-ci-registry/*" } ] }
Подключаем его к корзине:
[simterm]
$ aws s3api --profile tag-cn put-bucket-policy --bucket tag-ci-registry --policy file://tag-ci-registry-policy.json
[/simterm]
SSL
Следующим шагом – требуется добавить SSL сертификат, что бы Docker registry работал через внешний домен.
Создаём каталог для сертификата:
[simterm]
# mkdir -p /etc/ssl/cn/2017
[/simterm]
С рабочей машины загружаем сертификат и ключ:
[simterm]
$ scp -i ../../tag-server-api-infrastructure/.ssh/tag-cn.pem ssl_certificate.cer ubuntu@52.***.***.9:/home/ubuntu $ scp -i ../../tag-server-api-infrastructure/.ssh/tag-cn.pem star_cn-private-key_2017.key ubuntu@52.***.***.9:/home/ubuntu
[/simterm]
На CI хосте перемещаем их:
[simterm]
# mv ssl_certificate.cer /etc/ssl/cn/2017 # mv star_cn-private-key_2017.key /etc/ssl/cn/2017
[/simterm]
В файл /etc/docker/daemon.json
добавляем параметр insecure-registries
:
{ "data-root": "/docker/", "insecure-registries": ["registry-domain.cn:5000"] }
HTTP авторизация
Создаём каталог для файла htpasswd
:
[simterm]
# mkdir -p /etc/registry/auth
[/simterm]
И сам файл с пользователем и паролем, который будет использоваться для авторизации на Docker registry:
[simterm]
# docker run --entrypoint htpasswd daocloud.io/registry:2 -Bbn dockeruser dockerpass > /etc/registry/auth/htpasswd
[/simterm]
Docker registry config.yml
Следующим шагом – надо переопределить параметры storage
в /etc/docker/registry/config.yml
внутри контейнера с registry.
Создаём каталог:
[simterm]
# mkdir /etc/registry/conf
[/simterm]
В нём создаём файл config.yml
, в котором переопределяяем параметры в блоке storage
:
version: 0.1 log: fields: service: registry storage: s3: accesskey: AKI***L5A secretkey: OtN***7Cb region: cn-north-1 bucket: tag-ci-registry http: addr: :5000 headers: X-Content-Type-Options: [nosniff] health: storagedriver: enabled: true interval: 10s threshold: 3
Docker Compose для Docker Registry
Создаём файл, в котором указываем все каталоги для монтирования в контейнер и переменные для S3 и SSL:
registry: restart: always image: daocloud.io/registry:2 ports: - 5000:5000 environment: AWS_ACCESS_KEY_ID: AKI***L5A AWS_SECRET_ACCESS_KEY: OtN***7Cb REGISTRY_HTTP_TLS_CERTIFICATE: /certs/ssl_certificate.cer REGISTRY_HTTP_TLS_KEY: /certs/star_cn-private-key_2017.key REGISTRY_AUTH: htpasswd REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm volumes: - /etc/ssl/cn/2017:/certs - /etc/registry/auth:/auth - /etc/registry/conf/config.yml:/etc/docker/registry/config.yml
Запускаем:
[simterm]
# docker-compose up -d Starting registry_registry_1 ... Starting registry_registry_1 ... done
[/simterm]
Проверяем.
Создадим копию образа из уже скачанных:
[simterm]
# docker tag daocloud.io/registry:2 registry-domain.cn:5000/registry:2
[/simterm]
Пушим его:
[simterm]
# docker push registry-domain.cn:5000/registry:2 The push refers to repository [registry-domain.cn:5000/registry] 3c133a51bc00: Preparing a2717186d7dd: Preparing 656c7684d0bd: Preparing 7683d4fcdf4e: Preparing ef763da74d91: Preparing no basic auth credentials
[/simterm]
Гуд – требует авторизацию:
no basic auth credentials
Логинимся:
[simterm]
# docker login registry-domain.cn:5000 Username: dockeruser Password: Login Succeeded
[/simterm]
Пушим:
[simterm]
# docker push registry-domain.cn:5000/registry:2 The push refers to repository [registry-domain.cn:5000/registry] 3c133a51bc00: Pushed a2717186d7dd: Pushed 656c7684d0bd: Pushed 7683d4fcdf4e: Pushed ef763da74d91: Pushed 2: digest: sha256:435db1be85c6c10b2f506516aa14d8c485c1f1bd5f4a941a637808b085f294b6 size: 1364
[/simterm]
Проверяем содержимое корзины:
[simterm]
$ aws s3 --profile tag-cn ls --recursive s3://tag-ci-registry 2017-11-22 16:07:22 6265374 docker/registry/v2/blobs/sha256/3a/3a41740f900cbba03b223c025f03632db1284d85a696677bca8d6375cdf6040b/data 2017-11-22 16:07:23 1364 docker/registry/v2/blobs/sha256/43/435db1be85c6c10b2f506516aa14d8c485c1f1bd5f4a941a637808b085f294b6/data 2017-11-22 16:07:21 2385012 docker/registry/v2/blobs/sha256/49/49388a8c9c86a6f56d228954eede699c64fce6c671a989e3e21c391859694645/data 2017-11-22 16:07:20 213 docker/registry/v2/blobs/sha256/65/65f212f7c77805418cb85461d93a522c1bf4eebac73009ccd23ac2159ac33dad/data 2017-11-22 16:07:23 3165 docker/registry/v2/blobs/sha256/a0/a07e3f32a779aa924fd47f6797d4d5c93061c50c0eb97d464f08365a3a30200b/data 2017-11-22 16:07:20 371 docker/registry/v2/blobs/sha256/e1/e16ef4b766841014d6a902f034f0f67698bcbebc1b4c36ff3574d3730e79e2ee/data ...
[/simterm]
Всё отлично.
Docker Compose старт после reboot сервера
Сначала решил добавить systemd-юнит, как описано тут>>>, но – не потребовалось: хватает опции restart: "always"
в docker-compose.yml
обоих сервисов.
Проверяем:
[simterm]
ubuntu@ip-172-31-2-146:~$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6ab55042e375 daocloud.io/jenkins "/bin/tini -- /usr/l…" 2 minutes ago Up 2 minutes 50000/tcp, 0.0.0.0:80->8080/tcp jenkins_jenkins_1 f8d70fafbe68 daocloud.io/registry:2 "/entrypoint.sh /etc…" About an hour ago Up 2 minutes 0.0.0.0:5000->5000/tcp registry_registry_1
[/simterm]
Перезагружаем сервер:
[simterm]
ubuntu@ip-172-31-2-146:~$ sudo reboot Connection to 52.***.***.9 closed by remote host. Connection to 52.***.***.9 closed.
[/simterm]
После рестарта – логинимся, проверяем ещё раз:
[simterm]
... Last login: Wed Nov 22 14:36:07 2017 from 54.***.***.0 ubuntu@ip-172-31-2-146:~$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6ab55042e375 daocloud.io/jenkins "/bin/tini -- /usr/l…" 3 minutes ago Up 12 seconds 50000/tcp, 0.0.0.0:80->8080/tcp jenkins_jenkins_1 f8d70fafbe68 daocloud.io/registry:2 "/entrypoint.sh /etc…" About an hour ago Up 12 seconds 0.0.0.0:5000->5000/tcp registry_registry_1
[/simterm]
Готово.
По хорошему – сюда бы ещё добавить NGINX на входе, что бы подключить SSL для Jenkins – но уже не сегодня.