Quay는 기업 환경을 위한 분산 고가용성 컨테이너 이미지 레지스트리이다. On-Premises, Public Cloud 등 모든 컨테이너 환경 혹은 오케스트레이션 환경에서 작동가능한 것이 장점이다.
Quay는 git과 통합되어 빌드 자동화 진행, 보안성, 접근 제어, 컨텐츠 분산, 확장가능한 API와 대규모 스케일의 Test 등의 특징을 갖는다.
본 프로젝트에서는 앞서 구축한 openshift와 함께 CI/CD Pipeline을 구현하기 위해 Quay를 설치한다.
1. 논리적 Architecture
- Quay는 Openshift Container Platform의 외부 Node로써 구현
- Quay가 외부 Source Registry로부터 Image를 받아와 Mirroring한 뒤에 Clair을 통해 CVE Metadata를 Fetch하고 Image 취약점을 검사
- 본 프로젝트에서는 전체 리소스를 고려하여 Quay를 1중화로 진행
2. 물리적 Architecture(Quay Node 내부 컨테이너들간의 관계)
Quay
: Container Registry로서 Quay Container를 Pod의 여러 구성 요소로 구성된 서비스로 실행Database(Mysql)
: Quay에서 기본 Metadata Storage로 사용Redis(Key, Value Store)
: Live Builder Log 및 Red Hat Quay turorial 저장Clair
: 컨테이너 이미지의 취약점을 검사하고 수정 사항을 제안gitLab
: 형상 관리 도구Object Storage
: 오브젝트 형태의 데이터를 저장할 수 있는 스토리지로 본 프로젝트는 On-premises 환경이므로 Minio 사용
설치 환경
- quay : 3.2.1 v
- quay-builder : 3.2.1 v
- clair-jwt : 3.2.1 v
- External IP : 192.168.100.10/24 (NAT)
- Internal IP : 10.10.10.21
- 방화벽 해제
Quay의 Port Forwarding
- QuayBuilder : 80 Port
- Redis : 6379 Port
- MySQL : 3306 Port
- QuayConfig : 28443 Port
- Minio : 9000 Port
- Gitlab : 18443, 18080, 18022 Port
Clair의 Port Forwarding
- JWTproxy : 6060 Port
- pgsql : 5432 Port
- Postgresql : pgsql과 Link 연결
1. Quay 설치 사전 준비
-
Quay로 사용할 RHEL 7.8 OS의 가상머신을 생성한 뒤, 아래 과정 진행
-
RAM : 8 GB , vCPU : 3 Core , Disk : 60 GB
-
hostname 설정
# Quay Node의 Hostname 변경 hostnamectl set-hostname quay.redhat2.cccr.local # Quay Node와 Host PC에 각각 ip 추가 # 해당 IP로 웹 콘솔 접근 가능 vi /etc/hosts 192.168.100.10 quay.redhat2.cccr.local 192.168.100.10 clair.redhat2.cccr.local 192.168.100.10 gitlab.redhat2.cccr.local
-
RHEL OS 사용을 위해 Redhat 계정으로 Subscription을 등록
-
Subscription 등록 과정에서 Repository 활성화
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release subscription-manager register # RedHat 계정 입력 Username: ${USERNAME} Password: ${PASSWORD} The system has been registered with ID: ~~~~ The registered system name is: quay.redhat2.cccr.local subscription-manager list --available --matches '*RHEL*' subscription-manager attach --pool=<<Pool_ID>> subscription-manager repos --disable="*" subscription-manager repos --enable="rhel-7-server-rpms" --enable="rhel-7-server-extras-rpms" yum update -y # 업데이트 후, Server Reboot reboot
2. Docker 설치
-
Docker 설치
yum install docker device-mapper-libs device-mapper-event-libs** systemctl enable docker systemctl start docker systemctl is-active docker
-
Insecure Docker Registry 세팅
vi /etc/sysconfig/docker ... # 내용 추가 ADD_REGISTRY=--add-registry '<<quay FQDN>>' INSECURE_REGISTRY=--insecure-registry '<<quay FQDN>>'
3. Quay 인증
-
quay.io에서 Red Hat Quay V3 Container Image을 얻기 위하여 Key를 통해 인증
[root@quay ~]docker login -u="<<ID>>" -p="<<PASSWORD>>" quay.io
1. Database
-
Database 설치 디렉터리 생성
mkdir -p /var/lib/mysql chmod -R 777 /var/lib/mysql
-
환경변수 설정
export MYSQL_CONTAINER_NAME=mysql export MYSQL_DATABASE=quay export MYSQL_PASSWORD=quay export MYSQL_USER=quay export MYSQL_ROOT_PASSWORD=quay
-
Quay의 Database는 PostgreSQL나 MySQL 모두 사용 가능
-
본 프로젝트에서는 MySQL을 설치
docker run --detach --restart=always \ --env MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \ --env MYSQL_USER=${MYSQL_USER} \ --env MYSQL_PASSWORD=${MYSQL_PASSWORD} \ --env MYSQL_DATABASE=${MYSQL_DATABASE} \ --name ${MYSQL_CONTAINER_NAME} \ --publish 3306:3306 -v /var/lib/mysql:/var/lib/mysql/data:Z \ registry.access.redhat.com/rhscl/mysql-57-rhel7
-
mysql 접근 가능한지 확인
yum install -y mariadb mysql -h 192.168.100.10 -u root --password=quay Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 2 Server version: 5.7.24 MySQL Community Server (GPL) Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]> \q Bye
2. Redis
-
Redis 설치 디렉토리 생성
mkdir -p /var/lib/redis chmod -R 777 /var/lib/redis
-
Redis 설치
docker run -d --restart=always -p 6379:6379 \ --privileged=true -v /var/lib/redis:/var/lib/redis/data:Z \ registry.access.redhat.com/rhscl/redis-32-rhel7
-
Redis와 통신 가능한지 확인
yum install telnet -y telnet 192.168.100.10 6379 [root@quay ~]# telnet 192.168.100.10 6379 .. .. Escape character is '^]'. MONITOR +OK QUIT +OK Connection closed by foreign host.
3. Gitlab
-
Gitlab 설치 디렉토리 생성 및 권한 설정
# config, logs, data mkdir -p /srv/gitlab/{config,logs,data} chmod -R 777 /srv/gitlab chown 1000:1000 /srv/gitlab cd srv chcon -Rv -u system_u * chcon -Rv -t container_file_t * ls -lZ * drwxrwxr-x. root root system_u:object_r:container_file_t:s0 config drwxr-xr-x. root root system_u:object_r:container_file_t:s0 data drwxr-xr-x. root root system_u:object_r:container_file_t:s0 logs
-
Gitlab 설치
docker run --detach --hostname gitlab.redhat2.cccr.local \ --publish 18443:443 --publish 18080:80 --publish 18022:22 \ --name gitlab --restart always \ --volume /srv/gitlab/config:/etc/gitlab \ --volume /srv/gitlab/logs:/var/log/gitlab \ --volume /srv/gitlab/data:/var/opt/gitlab \ gitlab/gitlab-ce:latest
4. Minio
-
Minio 설치 디렉터리 생성
mkdir /mnt/minio chmod -R 777 /mnt/minio
-
Minio 설치
docker run -itd -p 9000:9000 \ --name minio1 --privileged=true \ -e "MINIO_ACCESS_KEY=minioadmin" \ -e "MINIO_SECRET_KEY=minioadmin" \ -v /mnt/minio:/data minio/minio server /data
-
Minio dash-board 접속 후, quay 전용 디렉터리 생성
5. quayconfig 기반 Red Hat Quay 설정
-
사전에 다른 Quay 컨테이너를 사용하여 사용할 Quay 구성 파일(config.yaml)과 인증파일(security_scanner.pem) 생성
-
Quay가 어떤 Redis, MySQL, Gitlab, Clair와 연동할 것인지, Quay Web Console 접속할 시 사용할 ID, Password 등에 대해 정의
-
구성파일 생성용 Quay 컨테이너 생성
docker run --privileged=true -p 28443:8443 -d quay.io/redhat/quay:v3.2.1 config password
-
Docker process가 ipv4의 ip를 갖도록 설정 (optional)
vi /etc/sysctl.conf net.ipv4.ip_forward = 1 systemctl restart network
-
quay 구성 도구가 실행중인 컨테이너의 URL(ex-https://quay.redhat2.cccr.local:28443/) 접속하여 새로운 quayconfig 생성
6. quayconfig 생성 ( Dashboard에서 진행 )
-
Setup (DB 정보 입력)
- Database Type : MySQL
- Database Server : 192.168.100.10:3306
- Username : quay
- Password : quay
- Database Name : quay
-
Super User 생성
- Username: admin
- Email address : [email protected]
- Password : xxxxxx
-
Server Configuration
- Server Hostname : quay.redhat2.cccr.local
-
Time Machine
- 따로 설정하지 않음
-
Redis
- Redis Hostname : 192.168.100.10
- Redis Port : 6379
-
Repository Mirroring
- Enable Repository Mirroring 선택
-
Security Scanner
-
Enable Security Scanning 선택
-
Security Scanner Endpoint : http://clair.redhat2.cccr.local:6060 ( clair container의 endpoint 기입 )
-
Authentication Key 다운로드
- Assign New Key
- key name : security_scanner Service Key
- 다운로드 한 키는 Clair 컨테이너의 /var/lib/clair-config에 복사
-
-
Application Registry
- Enable App Registry 선택
-
Registry Protocol Settings
- Restrict V1 Push Support 선택 해제
-
Minio 설정
- Storage Engine : Red Hat Openshift Container Storage (NooBaa S3)
- NooBaa Server Hostname : 192.168.100.10
- Custom Port(optional) : 9000
- Access Key : minioadmin
- Secret Key : minioadmin
- Bucket Name : quay
- Storage Directory : /registry
-
Gitlab
-
Application ID와 Secret이 필요하므로 Gitlab 웹 콘솔(http://gitlab.redhat2.cccr.local)에 접속 후 로그인
-
Settings → Application → 생성
- Name : testapp
- Redirect URI : http://gitalb.redhat2.cccr.local/testapp (테스트용으로 지정)
-
생성된 Application의 App ID와 Secret을 준비
-
Enable Gitlab Trigger을 체크하고 Endpoint, App ID, Secret 기입
-
-
다음 단계로 넘어가 설정 정보가 포함된 File을 local에 다운로드
7. Quay 설치 및 배포
-
앞서 만든 minio, quay (설정 컨테이너), gitlab, redis, mysql 컨테이너 확인
docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4720d202d0e3 minio/minio "/usr/bin/docker-e..." 18 minutes ago Up 18 minutes 0.0.0.0:9000->9000/tcp minio1 2575c3050175 quay.io/redhat/quay:v3.2.1 "/quay-registry/qu..." 31 minutes ago Up 31 minutes 7443/tcp, 8080/tcp, 0.0.0.0:28443->8443/tcp kind_pare 7a33ff0ce108 gitlab/gitlab-ce:latest "/assets/wrapper" 37 minutes ago Up 37 minutes (healthy) 0.0.0.0:18022->22/tcp, 0.0.0.0:18080->80/tcp, 0.0.0.0:18443->443/tcp gitlab 626c3eb9fd77 registry.access.redhat.com/rhscl/redis-32-rhel7 "container-entrypo..." 44 minutes ago Up 44 minutes 0.0.0.0:6379->6379/tcp stupefied_dubinsky 5905964d5a75 registry.access.redhat.com/rhscl/mysql-57-rhel7 "container-entrypo..." 46 minutes ago Up 46 minutes 0.0.0.0:3306->3306/tcp mysql
-
Quay에서 사용할 디렉토리를 생성
mkdir -p /mnt/quay/{config, storage}
-
Quay 설정파일 옮기기
-
앞서 local PC에 다운 받은 quay-config.tar.gz를 Quay vm 으로 이동
scp quay-config.tar.gz [email protected]:/mnt/quay/config/
-
Quay vm 에서 quay-config.tar.gz 압축 해제
cd /mnt/quay/config tar xvf quay-config.tar.gz chcon -Rv -u system_u *.yaml chcon -Rv -t container_file_t *
-
Quay 설치 및 배포
-
이 때, 컨테이너에 gitlab host 정보를 추가하여 builderworker가 quay와 같은 서버에 설치될 경우에 발생할 수 있는 no_such_host 에러를 방지
docker run --restart=always -p 443:8443 -p 80:8080 \ --add-host gitlab.redhat2.cccr.local:192.168.100.10 \ --sysctl net.core.somaxconn=4096 \ --privileged=true \ -v /mnt/quay/config:/conf/stack:Z \ -v /mnt/quay/storage:/datastorage:Z \ -d quay.io/redhat/quay:v3.2.1
-
Quay Super User 계정 ( ID: admin, Password: password )으로 Quay 콘솔(quay.redhat2.cccr.local)에 로그인하여 확인
8. Mirror Worker와 Builder 실행
-
Mirror worker 실행
docker run -d --name mirroring-worker -v /mnt/quay/config:/conf/stack:Z \ -d quay.io/redhat/quay:v3.2.1 repomirror
-
Builder 실행
-
환경 변수로 SERVER를 사용하여 worker가 Red Hat Quay에 접근할 수 있는 Host이름을 지정해야 하며, TLS를 사용하지 않는 경우 ws 파라미터와 함께 Port를 명시해야 함
docker run --restart on-failure \ -e SERVER=ws://192.168.100.10:80 \ --privileged=true \ -v /var/run/docker.sock:/var/run/docker.sock:Z \ -d quay.io/redhat/quay-builder:v3.2.1
- Openshift에서 Clair는 컨테이너 이미지를 스캔하여 취약점을 검사하고 수정 사항을 제안하는 역할을 수행
- Clair를 실행하려면 Database가 필요
- Clair의 Database로 MySQL은 지원하지 않으므로 본 프로젝트에서는 PostgreSQL로 구성
1. Clair 설치 작업 디렉터리 생성
- 작업 디렉터리 : /var/lib/clair-config
mkdir -p /var/lib/clair-config && cd /var/lib/clair-config chmod 777 /var/lib/clair-config
2. PostgreSQL 설치
-
pgsql Container
docker run -d -p 5432:5432 --name pgsql -e POSTGRES_PASSWORD=mysecretpassword \ -e POSTGRES_HOST_AUTH_METHOD=trust postgres
-
postgres Container
docker run --rm --link pgsql:postgres postgres \ sh -c 'echo "create database clairtest" | psql -h \ "$POSTGRES_PORT_5432_TCP_ADDR" -p \ "$POSTGRES_PORT_5432_TCP_PORT" -U postgres'
3. Clair Image Pull ( Security-enabled )
-
Clair 설정 파일( config.yaml )생성
vi /var/lib/clair-config/config.yaml # Example of clair config.yaml clair: database: type: pgsql options: source: postgresql://[email protected]:5432/clairtest? sslmode=disable cachesize: 16384 api: healthport: 6061 port: 6062 timeout: 900s paginationkey: updater: interval: 6h enabledupdaters: - debian - ubuntu - rhel - oracle - alpine - suse notifier: attempts: 3 renotifyinterval: 1h http: endpoint: http://quay.redhat2.cccr.local/secscan/notify proxy: http://localhost:6063 jwtproxy: signer_proxy: enabled: true listen_addr: :6063 ca_key_file: /certificates/mitm.key ca_crt_file: /certificates/mitm.crt signer: issuer: security_scanner expiration_time: 5m max_skew: 1m nonce_length: 32 private_key: type: autogenerated options: rotate_every: 12h key_folder: /clair/config/ key_server: type: keyregistry options: registry: http://quay.redhat2.cccr.local/keys/ verifier_proxies: - enabled: true listen_addr: :6060 verifier: audience: http://clair.redhat2.cccr.local:6060 upstream: http://localhost:6062 key_server: type: keyregistry options: registry: http://quay.redhat2.cccr.local/keys/
-
local에서 Quay vm의 /var/lib/clair-config 로 security_scanner.pem 파일 이동
-
Clair 관련 파일 권한 설정
# config.yaml chcon -Rv -u system_u *.yaml # security_scanner.pem chcon -Rv -u system_u *.pem # Context Change chcon -Rv -t container_file_t *
4. Clair 컨테이너 설치 및 배포
-
jwt(Json Web Token) : Json 포맷을 이용하여 사용자에 대한 속성을 저장하는 Claim 기반의 Web Token
docker run -d --restart=always -p 6060:6060 -p 6061:6061 \ --add-host quay.redhat2.cccr.local:192.168.100.10 \ --privileged=true -v /var/lib/clair-config:/clair/config \ quay.io/redhat/clair-jwt:v3.2.1
1. Quay 로 Docker 이미지 pull
- Quay Domain Name : quay.redhat2.cccr.local
- ubi7 이라는 Docker 이미지를 사용하여 test 진행
- 아래 명령어 실행 시, Quay 에서 repositories에서 /admin/ubi7 확인 가능
docker pull ubi7 # Namespace : admin, Repository : ubi7, Version : v0.1 docker tag ubi7 quay.redhat2.cccr.local/admin/ubi7:v0.1 docker push quay.redhat2.cccr.local/admin/ubi7:v0.1
2. 레포지토리에 Robot 계정 생성
- Quay 콘솔을 통해 Repositories-> /admin/ubi7-> Setting-> Robot 계정 생성
- Robot에게 ubi7 Repository에 대한 Read 권한 부여
3. Docker 이미지 Build
-
Dockerfile 준비
### Dockerfile FROM quay.redhat2.cccr.local/admin/ubi7:v0.1 RUN echo "Hello world" > /tmp/hello_world.txt CMD ["cat", "/tmp/hello_world.txt"\
-
Repository-> Build Triggers -> Start New Build -> Select file -> Dockerfile 에 앞서 만든 Dockerfile 선택 -> Start Build
-
DockerBuild 성공
4. 이미지 보안성 검사
- DockerBUild 성공 화면에서 설정(톱니바퀴)에서 Tag 이름을 v0.2로 수정
- Tag History 확인 및 빌드된 이미지의 보안성 체크 확인
-
tag 수정한 Docker 컨테이너가 잘 작동하는지 확인
docker run -it quay.redhat2.cccr.local/admin/ubi7:v0.2 Hello world docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 268c03123b2a quay.redhat2.cccr.local/admin/ubi7:v0.2 "cat /tmp/hello_wo..." 2 minutes ago Exited (0) 2 minutes ago infallible_wozniak
1. Quay DNS 설정
- bastion 노드를 Quay 의 DNS 서버로 설정하여 Disconnected 환경의 OCP Node와 통신이 가능하도록 설정
vi /etc/resolv.conf server 10.10.10.17 server 8.8.8.8
2. Quay 노드에서 oc 명령어 활성화
-
Bastion node 에서 활성화 했던 것과 동일한 방법으로 진행
-
단, Quay 노드는 인터넷이 되는 환경에서 사용되므로 oc command를 바로 다운 가능
-
명령으로 사용가능 하도록 환경변수 설정 경로 하위에 oc와 kubectl 기입
wget https://mirror.openshift.com/pub/openshift-v4/clients/ocp/4.4.17/openshift-client-linux-4.4.17.tar.gz tar -xvf openshift-client-linux-4.4.17.tar.gz README.md oc kubectl cp ./oc /usr/local/bin/ cp ./kubectl /usr/local/bin/ oc version Client Version: 4.4.17 Server Version: 4.4.17 Kubernetes Version: v1.17.1+20ba474 kubectl version Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0-4-g38212b5", GitCommit:"d89e458c3dff553f9a732b282830bfa9b4e0ab9b", GitTreeState:"clean", BuildDate:"2020-08-10T08:45:51Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"17+", GitVersion:"v1.17.1+20ba474", GitCommit:"20ba474", GitTreeState:"clean", BuildDate:"2020-08-10T09:03:30Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"} # /root 디렉터리에 kubeconfig 파일 위치하기 export KUBECONFIG=/root/kubeconfig # 이제 oc 명령어를 사용할 수 있음 oc whoami kube:admin oc get nodes NAME STATUS ROLES AGE VERSION master-1.redhat2.cccr.local Ready master 25d v1.17.1+20ba474 master-2.redhat2.cccr.local Ready master 25d v1.17.1+20ba474 ...
1. ~/.docker/config.json 확인
-
Docker clients용 .dockercfg ( $HOME/.docker/config.json )은 이전에 보안 또는 안전하지 않은 레지스트리에 로그인 한 경우 인증 정보를 저장하는 Docker credentials 파일
-
OCP의 내부 레지스트리가 아닌 보안 컨테이너 이미지를 가져오려면 Docker Credentials에서 pull secret을 생성하여 서비스 계정에 추가해야 함
cd .docker/ cat config.json { "auths": { "quay.io": { "auth": "cmVkaGF0K3F1YXk6TzgxV1NIUlNKUjE0VUFaQks1NEdRSEpTMFAxVjRDTFdBSlYxWDJDNFNEN0tPNTlDUTlOM1JFMTI2MTJYVTFIUg==" }, "quay.redhat2.cccr.local": { "auth": "YWRtaW46cGFzc3dvcmQ=" } } }
2. secret와 link 생성
- secret 생성
oc create secret generic 'secret 이름' \ --from-file=.dockerconfigjson=/root/.docker/config.json --type=kubernetes.io/dockerconfigjson secret/test created
- secret link 설정
# default : 기본 서비스 계정 oc secrets link default 'secret 이름' --for=pull # build 이미지를 push & pull 하는데 secret을 사용하려면 Pod 내부에 Secret을 마운트 하여 사용 oc secrets link builder 'secret 이름'
3. cluster yaml 파일 수정
-
OCP 콘솔 -> Search -> Image (Config) -> Cluster -> YAML 에 아래 내용 기입
# Exmaple allowedRegistriesForImport: - domainName: quay.redhat2.cccr.local insecure: true registrySources: insecureRegistries: - quay.redhat2.cccr.local