- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2020-09-11T08:19:47+00:00","","")
#mynavi(Gitea&DroneでCI/CD環境構築)
#setlinebreak(on);
#html(){{
<style>
.images img { border: 1px solid #333;}
</style>
}}
* 概要 [#u099f1bb]
#html(<div class="pl10">)
#TODO
Drone の pipeline から 同一ホスト上で稼働する別のコンテナの再起動などを行ったみた。
#html(</div>)
* 目次 [#n9ff6b65]
#contents
- 関連
-- [[Azureメモ]]
-- [[DroneでAzureリソースをデプロイする]]
-- [[Gitea&Drone&リバースプロキシ]]
-- [[Gitea&DroneでCI/CD環境構築]]
* .drone.yml [#d0802d70]
* 必要な設定 [#k61b3d91]
#html(<div class="pl10">)
drone のパイプラインから 他の docker コンテナの停止や起動などを行う場合、以下の設定が必要となる。
** /var/run/docker.sock のマウント [#xf6dcfc4]
#html(<div class="pl10">)
runner で /var/run/docker.sock を参照できる必要があるので為、以下の通り /var/run/docker.sock をマウントしておく。
尚、.drone.yml で volumes を使用するには、Droneの設定で対象リポジトリを信頼済み(Trusted)としておく必要がある。(この操作はDroneの管理者ユーザのみ可能)
※ [[DroneでAzureリソースをデプロイする(注意点)>DroneでAzureリソースをデプロイする#c24d7db3]]
#mycode2(){{
kind: pipeline
type: docker
name: default
steps:
- name: "build and push to registry"
image: docker/compose
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
:
volumes:
- name: dockersock
host:
path: /var/run/docker.sock
}}
#html(</div>)
** docker login [#ed56bd3c]
#html(<div class="pl10">)
プライベートなレジストリへの push や pull を行う前の docker login では コマンドにパスワードを含まない用に考慮する。
https://docs.docker.com/engine/reference/commandline/login/#provide-a-password-using-stdin に記載されている方法に習う場合、ホスト側に置いたパスワードファイルもマウントしておく。
#mycode2(){{
kind: pipeline
type: docker
name: default
steps:
- name: "build and push to registry"
image: docker/compose
volumes:
- name: registrypwd
path: /tmp/registry-password
commands:
# プライベートレジストリに docker login (パスワードはホスト側のファイルから取得)
- "cat /tmp/registry-password | docker login ${REGISTRY_NAME} --username ${REGISTRY_USER} --password-stdin"
:
volumes:
- name: registrypwd
host:
path: /path/to/registry-password
}}
#html(</div>)
#html(</div>)
* サンプル [#i1228891]
#html(<div class="pl10">)
** 環境イメージ [#x533d4c4]
#html(<div class="pl10">)
Azure 上に 以下の環境を作って検証してみた。
#html(<div class="images">)
&ref(azure_drone_image_vm_ver.png,nolink);
#html(</div>)
#html(</div>)
** 環境作成 [#ra6b1be2]
#html(<div class="pl10">)
#html(){{
<div id="tabs1">
<ul>
<li><a href="#tabs1-1">0_env.sh</a></li>
<li><a href="#tabs1-2">1_create_vm.sh</a></li>
<li><a href="#tabs1-3">2_setup_vm.tmpl</a></li>
</ul>
}}
// START tabs1-1
#html(<div id="tabs1-1">)
#mycode2(){{
#!/bin/bash
PREFIX=XXXXXXX
subscriptionId=XXXXXXXXXXXXXXXXXXXXXXXXXX
region=japanwest
# リソースグループ
resourceGroup=${PREFIX}Resources
# 仮想ネットワーク
vnetName=${PREFIX}VNet
vnetPrefix=10.1.0.0/16
# セキュリティグループ名
nsgName=${vnetName}SecGrp
nsgPubRuleName=${nsgName}PubRule
# 仮想マシンを配置するサブネット
vmSubnetName=${PREFIX}VmSubnet
vmSubnetPrefix=10.1.1.0/24
# 仮想マシンの情報
vmName=${PREFIX}Vm
vmImage=UbuntuLTS
vmIpAddress=10.1.1.5
vmUser=sample
# コンテナレジストリ名(Prefix)
registryName=${PREFIX}registry
}}
#html(</div>)
// END tabs1-1
// START tabs1-2
#html(<div id="tabs1-2">)
#mycode2(){{
#!/bin/bash
# 設定の読み込み
source 0_env.sh
# リソース作成
if [ "$1" == "--create" ]; then
# リソースグループの作成
echo "az group create"
az group create --name $resourceGroup --location $region
# NSG(ネットワークセキュリティグループの)作成
echo "az network nsg create"
az network nsg create --resource-group $resourceGroup --name $nsgName
# NSGルール(80番ポート)
echo "az network nsg rule create(80)"
az network nsg rule create \
--resource-group $resourceGroup --nsg-name $nsgName --name ${nsgPubRuleName}1\
--access Allow --protocol Tcp --direction Inbound --priority 100 \
--source-address-prefix Internet --source-port-range "*" --destination-port-range "80"
# NSGルール(443番ポート)
echo "az network nsg rule create(443)"
az network nsg rule create \
--resource-group $resourceGroup --nsg-name $nsgName --name ${nsgPubRuleName}2 \
--access Allow --protocol Tcp --direction Inbound --priority 101 \
--source-address-prefix Internet --source-port-range "*" --destination-port-range "443"
# NSGルール(22ポート)
echo "az network nsg rule create(ssh)"
az network nsg rule create \
--resource-group $resourceGroup --nsg-name $nsgName --name ${nsgPubRuleName}3 \
--access Allow --protocol Tcp --direction Inbound --priority 1000 \
--source-address-prefix Internet --source-port-range "*" --destination-port-range "22"
# 仮想ネットワーク 及び サブネット作成
echo "az network vnet create"
az network vnet create \
--name $vnetName --resource-group $resourceGroup \
--address-prefixes $vnetPrefix --network-security-group $nsgName \
--subnet-name $vmSubnetName --subnet-prefixes $vmSubnetPrefix
# コンテナレジストリ作成
echo "az acr create ( $registryName )"
az acr create -n $registryName -g $resourceGroup --sku standard --admin-enabled true
# レジストリユーザ名/パスワード取得
acr_credential="`az acr credential show --name $registryName -o table | tail -1`"
registryUser="`echo "$acr_credential" | awk '{print $1}'`"
registryPwd="`echo "$acr_credential" | awk '{print $2}'`"
# 初版のDockerイメージをビルドしてACRに登録
#echo "az acr build ( $appImageName )"
#echo az acr build --registry $registryName --image ${appImageName} -f ./sample-app1/Dockerfile ./sample-app1
#az acr build --registry $registryName --image ${appImageName} -f ./sample-app1/Dockerfile ./sample-app1
# VMセットアップスクリプト作成(環境変数を埋め込み)
cat 2_setup_vm.tmpl \
| sed "s/###REGISTRY_USER###/${registryUser}/g" \
| sed "s/###REGISTRY_PWD###/${registryPwd}/g" > 2_setup_vm.sh
chmod 755 2_setup_vm.sh
# 仮想マシンの作成
rm -rf ~/.ssh/id_rsa
rm -rf ~/.ssh/id_rsa.pub
echo "az vm create"
az vm create \
--resource-group $resourceGroup --name $vmName --image $vmImage --generate-ssh-keys \
--vnet-name $vnetName --subnet $vmSubnetName \
--private-ip-address $vmIpAddress --admin-username $vmUser \
--public-ip-address-dns-name `echo $vmName | tr '[A-Z]' '[a-z]'` \
--custom-data 2_setup_vm.sh
# ポート開放
echo "az vm open-port(80)"
az vm open-port --resource-group $resourceGroup --name $vmName --port 80 --priority 901
echo "az vm open-port(443)"
az vm open-port --resource-group $resourceGroup --name $vmName --port 443 --priority 902
# 生成されたSSH鍵を退避しておく
mkdir -p pem
if [ -e ~/.ssh/id_rsa ]; then
mv ~/.ssh/id_rsa ./pem/id_rsa_${vmName}
mv ~/.ssh/id_rsa.pub ./pem/id_rsa_${vmName}.pub
fi
# giteaのURLを表示
echo GITEA ... https://${vmName}.${region}.cloudapp.azure.com/gitea | tr [A-Z] [a-z]
echo DRONE ... https://${vmName}.${region}.cloudapp.azure.com | tr [A-Z] [a-z]
echo Sample App ... https://${vmName}.${region}.cloudapp.azure.com/sample-app1 | tr [A-Z] [a-z]
fi
# リソース削除
if [ "$1" == "--delete" ]; then
echo az group delete
az group delete --name $resourceGroup
fi
# URL表示
if [ "$1" == "--env" ]; then
echo GITEA ... https://${vmName}.${region}.cloudapp.azure.com/gitea | tr [A-Z] [a-z]
echo DRONE ... https://${vmName}.${region}.cloudapp.azure.com | tr [A-Z] [a-z]
echo Sample App ... https://${vmName}.${region}.cloudapp.azure.com/sample-app1 | tr [A-Z] [a-z]
fi
}}
#html(</div>)
// END tabs1-2
// START tabs1-3
#html(<div id="tabs1-3">)
#mycode2(){{
#!/bin/bash
apt-get update
apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io
curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
# set server name
region=japanwest
hostname=`hostname | tr [A-Z] [a-z]`
server_name="${hostname}.${region}.cloudapp.azure.com"
# make and change directory
mkdir -p /tmp/docker_container
cd /tmp/docker_container
#--------------------------------------
# wait until azure dns name is available.
# (If it is NG after waiting 5 minutes, give up..)
#--------------------------------------
for i in `seq 30`; do
x=`host $server_name`
if [ "$?" == "0" ]; then
break
fi
sleep 10
done
#--------------------------------------
# get ssl certificate.
#--------------------------------------
sudo docker run --rm -p 80:80 \
-v /etc/letsencrypt:/etc/letsencrypt \
-v /etc/letsencrypt/logs:/var/log/letsencrypt \
certbot/certbot certonly --standalone \
-d $server_name \
--register-unsafely-without-email \
--non-interactive --agree-tos \
--force-renewal \
--renew-by-default \
--preferred-challenges http
#--------------------------------------
# create default.conf for nginx.
#--------------------------------------
cat <<_EO_DEFAULT_CONF_>default.conf
# http
server{
listen 80;
listen [::]:80;
server_name ${server_name};
return 301 https://\$host\$request_uri;
}
# https
server{
listen 443 ssl;
listen [::]:443 ssl;
server_name ${server_name};
ssl_certificate /etc/letsencrypt/live/${server_name}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${server_name}/privkey.pem;
location /sample-app1/ {
proxy_pass http://sample-app1/;
}
location /gitea/ {
proxy_pass http://gitea-app:3000/;
}
location / {
proxy_pass http://drone-app/;
}
}
_EO_DEFAULT_CONF_
#--------------------------------------
# set REGISTRY_NAME to environments.
#--------------------------------------
#export REGISTRY_NAME=###REGISTRY_USER###.azurecr.io
#--------------------------------------
# make .env
#--------------------------------------
cat <<_EO_ENV_>.env
GITEA_HOST=${server_name}
GITEA_USER_GID=1000
GITEA_USER_UID=1000
GITEA_DB_HOST=gitea-db:5432
GITEA_DB_NAME=gitea
GITEA_DB_PASSWD=gitea
GITEA_DB_PASSWORD=gitea
GITEA_DB_USER=gitea
DRONE_HOST=${server_name}
DRONE_GITEA_CLIENT_ID=
DRONE_GITEA_CLIENT_SECRET=
DRONE_RPC_SECRET=secret
DRONE_RUNNER_CAPACITY=2
REGISTRY_NAME=###REGISTRY_USER###.azurecr.io
REGISTRY_USER=###REGISTRY_USER###
_EO_ENV_
#--------------------------------------
# registry password.
#--------------------------------------
echo "###REGISTRY_PWD###">registry-password
#
# make docker-compose.yml
#
cat <<_EO_DOCKER_COMPOSE_>docker-compose.yml
version: "3"
services:
gitea-proxy:
image: nginx:latest
hostname: gitea-proxy
container_name: gitea-proxy
ports:
- "80:80"
- "443:443"
volumes:
- ./default.conf:/etc/nginx/conf.d/default.conf
- /etc/letsencrypt:/etc/letsencrypt
links:
- gitea-app
depends_on:
- gitea-app
gitea-app:
image: gitea/gitea:latest
hostname: gitea-app
container_name: gitea-app
environment:
USER_UID: "\${GITEA_USER_UID}"
USER_GID: "\${GITEA_USER_GID}"
ROOT_URL: "https://\${GITEA_HOST}/gitea"
DB_TYPE: "postgres"
DB_HOST: "\${GITEA_DB_HOST}"
DB_NAME: "\${GITEA_DB_NAME}"
DB_USER: "\${GITEA_DB_USER}"
DB_PASSWD: "\${GITEA_DB_PASSWD}"
DISABLE_SSH: "true"
#SSH_DOMAIN: "\${GITEA_HOST}"
#SSH_PORT: "\${GITEA_SSH_PORT}"
SKIP_TLS_VERIFY: "true"
TZ: "Japan"
volumes:
- ./volumes/gitea-app:/data
links:
- gitea-db
gitea-db:
image: postgres:latest
hostname: gitea-db
container_name: gitea-db
volumes:
- ./volumes/gitea-db:/var/lib/postgresql/data
environment:
POSTGRES_DB: "\${GITEA_DB_NAME}"
POSTGRES_USER: "\${GITEA_DB_USER}"
POSTGRES_PASSWORD: "\${GITEA_DB_PASSWD}"
drone-app:
image: drone/drone:latest
hostname: drone-app
container_name: drone-app
environment:
links:
- gitea-app
depends_on:
- gitea-app
environment:
DOCKER_API_VERSION: "1.39"
DRONE_AGENT_ENABLED: "true"
DRONE_GITEA_SERVER: "https://\${GITEA_HOST}/gitea"
DRONE_GITEA_CLIENT_ID: "\${DRONE_GITEA_CLIENT_ID}"
DRONE_GITEA_CLIENT_SECRET: "\${DRONE_GITEA_CLIENT_SECRET}"
DRONE_RPC_SECRET: "\${DRONE_RPC_SECRET}"
DRONE_SERVER_HOST: "\${DRONE_HOST}"
DRONE_SERVER_PROTO: "https"
DRONE_USER_CREATE: "username:droneadmin,admin:true"
DRONE_GITEA_SKIP_VERIFY: "true"
DRONE_GITEA_ALWAYS_AUTH: "false"
DRONE_TLS_AUTOCERT: "false"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./volumes/drone-app:/var/lib/drone
drone-runner:
image: drone/drone-runner-docker:latest
hostname: drone-runner
container_name: drone-runner
links:
- drone-app
- gitea-app
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./.env:/etc/drone.env:ro
environment:
DOCKER_API_VERSION: "1.39"
DRONE_RPC_PROTO: "https"
#DRONE_RPC_HOST: "\${DRONE_HOST}:\${DRONE_PORT}"
DRONE_RPC_HOST: "\${DRONE_HOST}"
DRONE_RPC_SECRET: "\${DRONE_RPC_SECRET}"
DRONE_RUNNER_CAPACITY: "\${DRONE_RUNNER_CAPACITY}"
DRONE_RUNNER_NAME: "drone-runner"
DRONE_RUNNER_ENV_FILE: "/etc/drone.env"
DRONE_TRACE: "true"
DRONE_RPC_DUMP_HTTP: "true"
DRONE_RPC_DUMP_HTTP_BODY: "true"
#DRONE_RUNNER_NETWORKS: my-network
networks:
default:
external:
name: my_network
_EO_DOCKER_COMPOSE_
#--------------------------------------
# create docker network
#--------------------------------------
docker network create my_network
#--------------------------------------
# start sample-app1(dummy).
#--------------------------------------
docker run --rm --network my_network --hostname sample-app1 --name sample-app1 -d nginx
#--------------------------------------
# start ci/cd containers
#--------------------------------------
docker-compose up -d
}}
#html(</div>)
// END tabs1-3
#html(</div>)
// END tabs1
#html(<script>$(function() { $("#tabs1").tabs(); });</script>)
(1) Azure環境の作成
#myterm2(){{
./1_create_vm.sh --create
:
:
gitea ... https://xxxxxxxxx.japanwest.cloudapp.azure.com/gitea
drone ... https://xxxxxxxxx.japanwest.cloudapp.azure.com
sample app ... https://xxxxxxxxx.japanwest.cloudapp.azure.com/sample-app1
}}
(2) Gitea セットアップ
- 表示された Gitea のURLにアクセスして初期セットアップ
- OAuthアプリケーション登録、及び サンプルアプリ用のリポジトリ作成
- VM に sshログインし .env のクライアントシークレットID、キーを変更&br; ※ ssh鍵は ./pem配下に出来ている筈&br; ※ VMの IP は az vm list-ip-addresses -o table で確認。&br; ※ .env や docker-compose.yml は /tmp/docker_container 配下に出来ている筈。
※ [[Gitea&DroneでCI/CD環境構築#zf9f5c8d]] を参照。
(3) Droneコンテナ再作成
#myterm2(){{
docker-compose stop && docker-compose up --build -d
}}
(4) Drone の URL にアクセスしてサンプルアプリ用のリポジトリをActivate
※ [[Gitea&DroneでCI/CD環境構築#xdb9d209]] を参照。
#html(</div>)
** サンプルアプリの作成 [#ucbbbab1]
#html(<div class="pl10">)
上記で作成したサンプルアプリ用のリポジトリに以下のリソースを commit、push して Drone のパイプラインが正常に実行された事を確認後に、sample app の URLにアクセスして画面が表示( version 1 )されたらOK。
#html(){{
<div id="tabs2">
<ul>
<li><a href="#tabs2-1">Dockerfile</a></li>
<li><a href="#tabs2-2">docker-compose.yml</a></li>
<li><a href="#tabs2-3">index.html</a></li>
<li><a href="#tabs2-4">.drone.yml</a></li>
</ul>
}}
// START tabs2-1
#html(<div id="tabs2-1">)
#mycode2(){{
FROM nginx
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80
}}
#html(</div>)
// END tabs2-1
// START tabs2-2
#html(<div id="tabs2-2">)
#mycode2(){{
version: "3"
services:
sample-app1:
image: ${REGISTRY_NAME}/sample-app1:latest
hostname: sample-app1
container_name: sample-app1
#ports:
# - "8000:80"
networks:
default:
external:
name: my_network
}}
#html(</div>)
// END tabs2-2
// START tabs2-3
#html(<div id="tabs2-3">)
#mycode2(){{
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
</head>
<body>
<h1>Sample App1!</h1>
<div>version 1.</div>
</body>
</html>
}}
#html(</div>)
// END tabs2-3
// START tabs2-4
#html(<div id="tabs2-4">)
#mycode2(){{
kind: pipeline
type: docker
name: default
steps:
- name: "build and push to registry"
image: docker/compose
volumes:
- name: dockersock
path: /var/run/docker.sock
- name: containers
path: /tmp/docker_container
commands:
- "nowdate=`date +%Y%m%d_%H%M%S`"
- "appname=sample-app1"
- "cat /tmp/registry-password | docker login ${REGISTRY_NAME} --username ${REGISTRY_USER} --password-stdin"
- "cat /tmp/docker_container/registry-password | docker login ${REGISTRY_NAME} --username ${REGISTRY_USER} --password-stdin"
- "docker build -t $appname ."
- "docker tag $appname $REGISTRY_NAME/$appname:latest"
- "docker tag $appname $REGISTRY_NAME/$appname:$nowdate"
- "docker push $REGISTRY_NAME/$appname:latest"
- "docker push $REGISTRY_NAME/$appname:$nowdate"
when:
branch:
- master
- name: "docker pull and restart"
image: docker/compose
volumes:
- name: dockersock
path: /var/run/docker.sock
- name: registrypwd
path: /tmp/registry-password
- name: containers
path: /tmp/docker_container
commands:
- "appname=sample-app1"
- "cat /tmp/registry-password | docker login ${REGISTRY_NAME} --username ${REGISTRY_USER} --password-stdin"
- "cat /tmp/docker_container/registry-password | docker login ${REGISTRY_NAME} --username ${REGISTRY_USER} --password-stdin"
- "if [ `docker ps | grep $appname | wc -l` != \"0\" ]; then docker stop $appname ; fi"
- "if [ `docker ps -a | grep $appname | wc -l` != \"0\" ]; then docker rm $appname ; fi"
- "if [ `docker images | grep ${REGISTRY_NAME}/${appname}:latest | wc -l` != \"0\" ]; then docker rmi ${REGISTRY_NAME}/${appname}:latest; fi"
- "docker-compose up --build -d"
when:
branch:
- master
volumes:
- name: dockersock
host:
path: /var/run/docker.sock
- name: registrypwd
- name: containers
host:
path: /path/to/registry-password
path: /tmp/docker_container
}}
#html(</div>)
// END tabs2-4
#html(</div>)
// END tabs2
#html(<script>$(function() { $("#tabs2").tabs(); });</script>)
#html(</div>)
#html(</div>)