Rundeckについて
公式ページ
ジョブスケジューラサーバソフト。
有償版のRundeck Proと無償のオープンソース版のRundeck Communityがある。
以下のような特徴がある。
- エージェントレス
エージェントレスのため、リモートサーバでのジョブ実行を行う場合、SSHでログインして、必要なファイルをダウンロードし、スクリプトを実行することはできる - Javaで動作する
Web UIを持つが、Tomcatなどのサーブレットコンテナは不要である - Linux / Windows / Macなどマルチプラットフォーム対応
参考:https://docs.rundeck.com/job-scheduler/
用語
サーバー構築
構築環境
- Rundeck
3.2.9
パッケージ版は3.0.9
- OS
CentOS 7.5
OS設定
ネットワーク
固定IPアドレス割り当て
次のファイルを編集する
# ifcfg-loはループバックインタフェースであるため、触らない vi /etc/sysconfig/network-scripts/ifcfg-<インタフェース名>
以下では例として192.168.0.51/24を割り当てた
BOOTPROTO=static ONBOOT=yes IPADDR=192.168.0.51 NETMASK=255.255.255.0 NETWORK=192.168.0.0 GATEWAY=192.168.0.1
ネットワークを再起動する
systemctl restart network
ip addressコマンドにより、インタフェースが有効になっていることを確認する
これによりホストマシンなどからSSHでログイン可能となる
firewalld
HTTPを開ける
- 現在ルールが定義されていることを確認
firewall-cmd --permanent --info-service=http
- 現在ルールが適用されていないことを確認する
firewall-cmd --list-services --zone=public --permanent | sed -e "s/ /\n/g" | grep http
- ルールを適用する
firewall-cmd --add-service=http --zone=public --permanent
- ルールが適用されていることを確認する
firewall-cmd --list-services --zone=public --permanent | sed -e "s/ /\n/g" | grep http
- 設定を再読み込みして反映させる
firewall-cmd --reload
HTTPSも開ける場合
- 現在ルールが定義されていることを確認
firewall-cmd --permanent --info-service=https
- 現在ルールが適用されていないことを確認する
firewall-cmd --list-services --zone=public --permanent | sed -e "s/ /\n/g" | grep https
- ルールを適用する
firewall-cmd --add-service=https --zone=public --permanent
- ルールが適用されていることを確認する
firewall-cmd --list-services --zone=public --permanent | sed -e "s/ /\n/g" | grep https
- 設定を再読み込みして反映させる
firewall-cmd --reload
Rundeck用ポートを開ける場合
RundeckはデフォルトではTCP/4440を使用する。ApacheなどのWebサーバを使用せず、かつRundeckのデフォルトポートを使用する場合は、このポートを開放する必要がある。
- 現在ルールが定義されていないか確認
firewall-cmd --permanent --info-service=tomcat
- 新しいルールを定義
firewall-cmd --new-service=rundeck --permanent firewall-cmd --service=rundeck --add-port=4440/tcp --permanent
- ルールが定義されていることを確認
firewall-cmd --permanent --info-service=rundeck
- 現在ルールが適用されていないことを確認する
firewall-cmd --list-services --zone=public --permanent | sed -e "s/ /\n/g" | grep rundeck
- ルールを適用する
firewall-cmd --add-service=rundeck --zone=public --permanent
- ルールが適用されていることを確認する
firewall-cmd --list-services --zone=public --permanent | sed -e "s/ /\n/g" | grep rundeck
- 設定を再読み込みして反映させる
firewall-cmd --reload
ホスト名
ホスト名としてrundeckを設定する
# 再起動しても有効にする hostnamectl set-hostname rundeck.corp
再ログインすることで有効になる。
ユーザ作成
Rundeck用のユーザを作成する
useradd --user-group --no-create-home --shell /sbin/false rundeck
Javaのインストール
Rundeckの構築
ソースコードからインストール
パッケージからインストール
- rundeckをインストールする
rpm -Uvh http://repo.rundeck.org/latest.rpm yum install rundeck -y
- 管理用Webページのドメイン名を変更する
vi /etc/rundeck/rundeck-config.properties
# “grails.serverURL=http://localhost:4440″のlocalhost部分をサーバのIPかFQDNに変更する
使用しているIPアドレスの内、初めのもので良いなら下記を実行するのでも良い
sed "s/localhost/`ip a | egrep "[0-9]+\.[0-9]+\." | egrep -v "127\.0\." | head -n 1 | sed -e "s/\/.*//" -e "s/.* //"`/" -i /etc/rundeck/rundeck-config.properties
- サービスを有効化する
systemctl enable rundeckd
- サービスを起動する
systemctl start rundeckd
- 稼働確認
systemctl status rundeckd
正常に起動していない場合、ログを確認する。
journalctl -u rundeckd cat /var/log/messages
- RundeckがインストールされたサーバにWebブラウザでアクセスする
http://<IPアドレス>:4440/ - ログインする
admin / admin
CLIのインストール
yum install rundeck-cli -y
export RD_CONF=/path/to/rd.conf
cat > ~/.rd/rd.conf << EOT
export RD_URL=http://127.0.0.1:4440
export RD_USER=admin
export RD_PASSWORD=admin
EOT
rd projects list
rd jobs list -p TestProject1
ジョブのバックアップ
rd jobs list -p TestProject1 -f backup.xml
https://rundeck.github.io/rundeck-cli/configuration/
https://rundeck.github.io/rundeck-cli/commands/
設定
初期設定
パッケージ版
#loglevel.default is the default log level for jobs: ERROR,WARN,INFO,VERBOSE,DEBUG loglevel.default=INFO rdeck.base=/var/lib/rundeck #rss.enabled if set to true enables RSS feeds that are public (non-authenticated) rss.enabled=false # change hostname here grails.serverURL=http://localhost:4440 dataSource.dbCreate = update dataSource.url = jdbc:h2:file:/var/lib/rundeck/data/rundeckdb;MVCC=true rundeck.log4j.config.file = /etc/rundeck/log4j.properties
運用
プロジェクト
作成
- 管理画面にログインする
http://<RundeckServer>:4440/menu/home - New Project
- 各フィールドを入力する
- Project Name
プロジェクトの識別名。文字と数字のみ入力可能。 - Label
プロジェクトの表示名。 - Description
プロジェクトの説明。 - Execution Mode
- Disable Execution
CLIコマンドからの実行を禁止するかどうか - Disable Schedule
スケジュール実行機能を無効にするかどうか
- Disable Execution
- User Interface
- Job Group Expansion Level
ジョブページにおいて、デフォルトでジョブ階層をどこまで展開して表示するか。
展開しない場合は0を、すべて展開する場合は-1を指定する。 - Display the Project Readme
プロジェクト一覧ページ、ホーム画面にこのプロジェクトのReadmeを表示するかどうか - Display the MOTD
- Job Group Expansion Level
- Default Node Executor
デフォルトの実行タイプ
- SSH
Executes a command on a remote node via SSH. - Local
Executes commands locally on the Rundeck server - Stub
Prints the command instead of executing it. (Useful for mocking processes.) - Script Execution
Delegates command execution to an external script. Can be configured project-wide or on a per-node basis. - Ansible Ad-Hoc Node Executor
Runs Ansible Ad-Hoc commands on the nodes using the shell module. - openssh / executor
Executes a openssh command on the node. Password Authentication needs sshpass installed on the rundeck server - WinRM Node Executor Python
Executing Scripts or Commands on remote Windows computer
- SSH
- Default Node File Copier
リモートホストでジョブを実行する際に実行スクリプトの転送方式のデフォルト。
- Project Name
- Create
- 追加設定を行う
- Use Asynchronous Cache
Use asynchronous cache for all Resource Model Source results in this project - Synchronous First Load
When the cache is empty, forces the first load to happen synchronously to prevent empty node results.
- Use Asynchronous Cache
- Save
ジョブ
ジョブはデフォルトではrundeckユーザで実行される
作成
- ジョブを作成したいプロジェクトの”Create Job”
- New Job
- ジョブ情報を入力する
- Create
実行
手動実行
- メニューから”JOBS”
- 実行したいジョブの実行ボタンをクリックする
API
バージョン
APIにはバージョンがあり、バージョンにより応答内容が異なる。
バージョンは指定できるので、新バージョンが出ても旧バージョンは維持される。
バージョンは下記のように指定する。
http://localhost:4440/api/<バージョン>/
以下ではバージョンは27とする。
認証
トークン認証
トークンを発行することで、そのトークンを付加しAPIにアクセスすることができる
ただし、トークンは一時用途で、最長で30日間しか使用できない
トークンの発行方法は以下である。
- ユーザアイコン
- Profile
- “User API Tokens”で”+”ボタンをクリック
- トークン情報を入力する
- User
トークンに紐づけるユーザ名を入力する。
デフォルトではWeb管理画面の操作ユーザである。 - Roles
実行可能な権限を指定する。
未入力の場合、そのユーザの全ての権限を付与する。
コンマ区切りで複数入力可能。
- 指定可能値
- build
- architect
- admin
- user
- deploy
- 指定可能値
- Expiration in
有効期限
- User
- Generate New Token
- Show Token
- トークンを控える
パスワード認証
一旦ログインページにアクセスしてログインを行いCookieを取得する。
そのCookieを用いてAPIにアクセスする。
プロジェクト
一覧の取得
curl -H 'Content-Type: application/json' -H 'X-RunDeck-Auth-Token: <トークン>' http://localhost:4440/api/1/project
ジョブ
一覧の取得
curl -H 'Content-Type: application/json' -H 'X-RunDeck-Auth-Token: <トークン>' http://localhost:4440/api/27/project/<プロジェクト識別名>/jobs
トークン
curl -H 'Content-Type: application/json' -H 'X-RunDeck-Auth-Token: <トークン>' http://localhost:4440/api/27/tokens
ログ
ファイル配置
パッケージ版
- 本体のログ
/var/log/rundeck
- ジョブのログ
/var/lib/rundeck/logs/rundeck
ジョブ実行ログ
定期削除
ジョブの実行ログは定期的に削除されず、基本的には手動で削除しなければならない。
ジョブの実行ログディレクトリ配下の一定日数経過後のファイルを削除することもできるが、公式にはファイルを直接消さずにRUndeckを操作して削除するべきである。
もっとも簡単なのはAPIを使用することである。
下記pythonスクリプトにより一定日数以降のログを削除可能である。
- 参考
vi deleteoldlogs.py # 下記を張り付け # 90日以上前のログを削除 python -m pip install requests python deleteoldlogs.py 90
- スクリプト内容
import requests import xml.etree.ElementTree as ET import time import sys API_KEY='xx' RUNDECKSERVER = 'http://127.0.0.1' RUNDECKPORT='4440' RUNDECKURI='' EXPIRE_DAYS = 30 TODAY = int(round(time.time() * 1000)) EXPIRE_MILISECONDS = EXPIRE_DAYS * 24 * 60 * 60 * 1000 # API call to get the list of the existing projects on the server. def listProjects(): url = RUNDECKSERVER +':'+RUNDECKPORT+RUNDECKURI+'/api/1/projects' headers = {'Content-Type': 'application/json','X-RunDeck-Auth-Token': API_KEY } r = requests.get(url, headers=headers, verify=False) return r.text.encode('utf-8') # Returns list of all the project names def getProjectNames(projectsinfo_xml): project_names = [] root = ET.fromstring(projectsinfo_xml) for projects in root: for name in projects.findall('project'): project_names.append(name.find('name').text) return project_names # API call to get the list of the jobs that exist for a project. def listJobsForProject(project_mame): url = RUNDECKSERVER +':'+RUNDECKPORT+RUNDECKURI+'/api/1/jobs' payload = { 'project': project_mame } headers = {'Content-Type': 'application/json','X-RunDeck-Auth-Token': API_KEY } r = requests.get(url, params=payload, headers=headers, verify=False) return r.text.encode('utf-8') # Returns list of all the jobids def getJobIDs(jobsinfo_xml): job_ids = [] root = ET.fromstring(jobsinfo_xml) for jobs in root: for job in jobs: job_ids.append(job.attrib['id']) return job_ids # API call to get the list of the executions for a Job. def getExecutionsForAJob(job_id): url = RUNDECKSERVER +':'+RUNDECKPORT+RUNDECKURI+'/api/1/job/'+job_id+'/executions' headers = {'Content-Type': 'application/json','X-RunDeck-Auth-Token': API_KEY } r = requests.get(url, headers=headers, verify=False) return r.text.encode('utf-8') # Returns a dict {'execution_id01': 'execution_date01', 'execution_id02': 'execution_date02', ... } def getExecutionDate(executionsinfo_xml): execid_dates = {} root = ET.fromstring(executionsinfo_xml) for executions in root: for execution in executions.findall('execution'): execution_id = execution.get('id') for date in execution.findall('date-ended'): execution_date = date.get('unixtime') execid_dates[execution_id] = execution_date return execid_dates #API call to delete an execution by ID def deleteExecution(execution_id): url = RUNDECKSERVER +':'+RUNDECKPORT+RUNDECKURI+'/api/12/execution/'+execution_id headers = {'Content-Type': 'application/json','X-RunDeck-Auth-Token': API_KEY } r = requests.delete(url, headers=headers, verify=False) def isOlderThanExpireDays(execution_date, today): if ((today - execution_date) > EXPIRE_MILISECONDS): return True return False def checkDeletion(execid_dates): for exec_id, exec_date in execid_dates.iteritems(): if isOlderThanExpireDays(int(exec_date), TODAY): deleteExecution(exec_id) args = sys.argv if len(args)>=2: try: EXPIRE_DAYS=int(args[1]) EXPIRE_MILISECONDS = EXPIRE_DAYS * 24 * 60 * 60 * 1000 except ValueError: print("Bad Argument : EXPIRE_DAYS must be Integer") sys.exit(1) projects = getProjectNames(listProjects()) for project in projects: print 'project:\t'+project jobids = getJobIDs(listJobsForProject(project)) for jobid in jobids: print '\tjobid:\t'+jobid checkDeletion(getExecutionDate(getExecutionsForAJob(jobid)))
起動スクリプト
/etc/init.d/rundeckd #!/bin/bash # # rundeckd Startup script for the rundeck # # chkconfig: 2345 90 10 # description: rundeckd, providing rundeckd # pidfile: /var/run/rundeckd.pid # Source function library prog="rundeckd" RETVAL=0 PID_FILE=/var/run/${prog}.pid servicelog=/var/log/rundeck/service.log . /etc/rc.d/init.d/functions . /etc/rundeck/profile start() { status -p $PID_FILE $prog >/dev/null RETVAL=$? if [ $RETVAL -eq 0 ]; then echo Already started. return $RETVAL fi echo -n $"Starting $prog: " if ! touch $servicelog; then echo No access to $servicelog. This usually means you need to be root echo_failure echo return 1 fi nohup runuser -s /bin/bash -l rundeck -c "$rundeckd" >>$servicelog 2>&1 & RETVAL=$? PID=$! echo $PID > $PID_FILE if [ $RETVAL -eq 0 ]; then touch /var/lock/subsys/$prog echo_success else echo_failure fi echo return $RETVAL } stop() { echo -n $"Stopping $prog: " killproc -p $PID_FILE "$rundeckd" RETVAL=$? echo [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog return $RETVAL } case "$1" in start) start ;; stop) stop ;; restart) stop start ;; condrestart) if [ -f /var/lock/subsys/$prog ]; then stop start fi ;; status) status -p $PID_FILE $prog RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|restart|condrestart|status}" RETVAL=1 esac exit $RETVAL
設定
ファイル構成
パッケージ
- 各プロジェクトの設定ファイル
/var/rundeck/
- Rundeck本体の設定ファイル
/etc/rundeck
設定ファイル概要
Rundeck本体の設定ファイル
- admin.aclpolicy
- apitoken.aclpolicy
- artifact-repositories.yaml
- cli-log4j.properties
- framework.properties
- jaas-loginmodule.conf
- log4j.properties
- profile
デーモン用の設定ファイル
- project.properties
- realm.properties
- rundeck-config.properties
- ssl/ssl.properties
デーモン設定
profile
初期設定
######### # Rundeck Profile sourced from /etc/rc.d/init.d/rundeckd ######### # # NOTE: DO NOT MODIFY THIS FILE # It will be replaced when the package is upgraded and your changes will not be saved. # # ################## # # To override variables in this file, you can instead create a file at: # # # Centos/Redhat default: # # /etc/sysconfig/rundeckd # # Or # # # Ubuntu/Debian default: # # /etc/default/rundeckd # # which contains exports for any of the variables listed below. E.g.: # # RUNDECK_TEMPDIR=/path/to/tmpdir # # That file will be sourced before this one, allowing your exports to take precedence. # ############### prog="rundeckd" [ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog [ -e /etc/default/$prog ] && . /etc/default/$prog RDECK_INSTALL="${RDECK_INSTALL:-/var/lib/rundeck}" RDECK_BASE="${RDECK_BASE:-/var/lib/rundeck}" RDECK_CONFIG="${RDECK_CONFIG:-/etc/rundeck}" RDECK_CONFIG_FILE="${RDECK_CONFIG_FILE:-$RDECK_CONFIG/rundeck-config.properties}" RDECK_SERVER_BASE="${RDECK_SERVER_BASE:-$RDECK_BASE}" RDECK_SERVER_CONFIG="${RDECK_SERVER_CONFIG:-$RDECK_CONFIG}" RDECK_SERVER_DATA="${RDECK_SERVER_DATA:-$RDECK_BASE/data}" RDECK_PROJECTS="${RDECK_PROJECTS:-$RDECK_BASE/projects}" RUNDECK_TEMPDIR="${RUNDECK_TEMPDIR:-/tmp/rundeck}" RUNDECK_WORKDIR="${RUNDECK_TEMPDIR:-$RDECK_BASE/work}" RUNDECK_LOGDIR="${RUNDECK_LOGDIR:-$RDECK_BASE/logs}" RDECK_JVM_SETTINGS="${RDECK_JVM_SETTINGS:- -Xmx1024m -Xms256m -XX:MaxMetaspaceSize=256m -server}" RDECK_TRUSTSTORE_FILE="${RDECK_TRUSTSTORE_FILE:-$RDECK_CONFIG/ssl/truststore}" RDECK_TRUSTSTORE_TYPE="${RDECK_TRUSTSTORE_TYPE:-jks}" JAAS_LOGIN="${JAAS_LOGIN:-true}" JAAS_CONF="${JAAS_CONF:-$RDECK_CONFIG/jaas-loginmodule.conf}" LOGIN_MODULE="${LOGIN_MODULE:-RDpropertyfilelogin}" RDECK_HTTP_PORT=${RDECK_HTTP_PORT:-4440} RDECK_HTTPS_PORT=${RDECK_HTTPS_PORT:-4443} # If no JAVA_CMD, try to find it in $JAVA_HOME if [ -z "$JAVA_CMD" ] && [ -n "$JAVA_HOME" ] && [ -x "$JAVA_HOME/bin/java" ] ; then JAVA_CMD=$JAVA_HOME/bin/java PATH=$PATH:$JAVA_HOME/bin export JAVA_HOME elif [ -z "$JAVA_CMD" ] ; then JAVA_CMD=java fi # build classpath without lone : that includes . for jar in $(find $RDECK_INSTALL/cli -name '*.jar') ; do CLI_CP=${CLI_CP:+$CLI_CP:}$jar done for war in $(find $RDECK_INSTALL/bootstrap -name '*.war') ; do EXECUTABLE_WAR=$war done RDECK_JVM="-Drundeck.jaaslogin=$JAAS_LOGIN \ -Djava.security.auth.login.config=$JAAS_CONF \ -Dloginmodule.name=$LOGIN_MODULE \ -Drdeck.config=$RDECK_CONFIG \ -Drundeck.server.configDir=$RDECK_SERVER_CONFIG \ -Dserver.datastore.path=$RDECK_SERVER_DATA/rundeck \ -Drundeck.server.serverDir=$RDECK_INSTALL \ -Drdeck.projects=$RDECK_PROJECTS \ -Drdeck.runlogs=$RUNDECK_LOGDIR \ -Drundeck.config.location=$RDECK_CONFIG_FILE \ -Djava.io.tmpdir=$RUNDECK_TEMPDIR \ -Drundeck.server.workDir=$RUNDECK_WORKDIR \ -Dserver.http.port=$RDECK_HTTP_PORT \ -Drdeck.base=$RDECK_BASE" # # Set min/max heap size # RDECK_JVM="$RDECK_JVM $RDECK_JVM_SETTINGS" # # SSL Configuration - Uncomment the following to enable. Check SSL.properties for details. # if [ -n "$RUNDECK_WITH_SSL" ] ; then RDECK_SSL_OPTS="${RDECK_SSL_OPTS:- -Djavax.net.ssl.trustStore=$RDECK_TRUSTSTORE_FILE -Djavax.net.ssl.trustStoreType=$RDECK_TRUSTSTORE_TYPE -Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol}" RDECK_JVM="$RDECK_JVM -Drundeck.ssl.config=$RDECK_SERVER_CONFIG/ssl/ssl.properties -Dserver.https.port=${RDECK_HTTPS_PORT} ${RDECK_SSL_OPTS}" fi unset JRE_HOME umask 002 rundeckd="$JAVA_CMD $RDECK_JVM $RDECK_JVM_OPTS -jar $EXECUTABLE_WAR --skipinstall"