GitHub Actions ਤੁਹਾਡੇ VPN ਤੱਕ ਨਹੀਂ ਪਹੁੰਚਦਾ: ਆਪਣਾ runner ਜਾਂ ਟਨਲ
ਹੋਸਟ ਕੀਤਾ runner ਤੁਹਾਡੇ ਨੈੱਟਵਰਕ ਤੋਂ ਬਾਹਰ ਇੱਕ ਨਵਾਂ VM ਹੈ। ਜਾਂ ਤਾਂ ਉਸ ਨੈੱਟਵਰਕ ਵਿੱਚ runner ਰੱਖੋ, ਜਾਂ job ਤੋਂ ਟਨਲ ਬਣਾਓ ਅਤੇ ਅਸਲੀ ਰੂਟ ਦੀ ਉਡੀਕ ਕਰੋ।
ਤੁਹਾਡਾ deploy ਕਦਮ ਕਿਸੇ ਅੰਦਰੂਨੀ hostname ਉੱਤੇ ਅਟਕ ਜਾਂਦਾ ਹੈ, ਜਦਕਿ ਉਹੀ ਕਮਾਂਡ ਤੁਹਾਡੇ ਲੈਪਟਾਪ ਤੋਂ ਤੁਰੰਤ ਜਵਾਬ ਦਿੰਦੀ ਹੈ, ਅਤੇ ਦੋਵੇਂ ਮਸ਼ੀਨਾਂ ਉੱਤੇ client build ਇੱਕੋ ਜਿਹਾ ਹੈ। job GitHub-ਹੋਸਟ ਕੀਤੇ runner ਉੱਤੇ ਚੱਲ ਰਿਹਾ ਹੈ: ਉਸੇ ਇੱਕ job ਲਈ GitHub ਦੇ ਕਲਾਊਡ ਵਿੱਚ ਬਣਾਈ ਵਰਚੁਅਲ ਮਸ਼ੀਨ, ਤੁਹਾਡੇ ਨਿੱਜੀ ਪਤਾ ਖੇਤਰ ਵਿੱਚ ਕੋਈ ਰੂਟ ਨਹੀਂ, ਅੰਦਰੂਨੀ ਜ਼ੋਨ ਲਈ ਕੋਈ resolver ਐਂਟਰੀ ਨਹੀਂ ਅਤੇ ਕੋਈ ਸਰਟੀਫਿਕੇਟ ਨਹੀਂ ਜੋ ਨਿੱਜੀ endpoint ਸਵੀਕਾਰ ਕਰੇ। ਇਸ ਸੀਮਾ ਵਿੱਚ ਸਿਰਫ਼ ਦੋ ਡਿਜ਼ਾਈਨ ਫਿੱਟ ਹੁੰਦੇ ਹਨ, ਅਤੇ workflow ਦੁਬਾਰਾ ਬਣਾਏ ਬਿਨਾਂ ਕੋਈ ਵੀ ਬਾਅਦ ਵਿੱਚ ਚਾਲੂ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। YAML ਦੀ ਪਹਿਲੀ ਲਾਈਨ ਤੋਂ ਪਹਿਲਾਂ ਇਹਨਾਂ ਵਿੱਚੋਂ ਚੁਣੋ, ਕਿਉਂਕਿ ਬਾਅਦ ਦਾ ਸਥਾਨਾਂਤਰਣ ਪਹਿਲੀ ਕੋਸ਼ਿਸ਼ ਨਾਲੋਂ ਵੱਧ ਮਹਿੰਗਾ ਪੈਂਦਾ ਹੈ।
ਚੋਣ ਉਸ runner ਵਿਚਕਾਰ ਹੈ ਜੋ ਉੱਥੇ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ ਜਿੱਥੇ ਨੈੱਟਵਰਕ ਪਹਿਲਾਂ ਹੀ ਖ਼ਤਮ ਹੁੰਦਾ ਹੈ, ਅਤੇ ਉਸ ਟਨਲ ਵਿਚਕਾਰ ਜੋ ਇੱਕ job ਜਿੰਨੀ ਚੱਲਦੀ ਹੈ। ਦੋਵੇਂ ਸਾਲਾਂ ਤੋਂ production deploy ਚੁੱਕ ਰਹੇ ਹਨ, ਅਤੇ ਵੱਖ-ਵੱਖ ਤਰੀਕਿਆਂ ਨਾਲ ਫੇਲ੍ਹ ਹੁੰਦੇ ਹਨ।
ਸੀਮਾ ਅਸਲ ਵਿੱਚ ਕਿੱਥੇ ਹੈ
ਤੁਸੀਂ ਲੈਪਟਾਪ ਉੱਤੇ ਜੋ ਵੀ ਇੰਸਟਾਲ ਕਰਦੇ ਹੋ ਉਹ ਲੈਪਟਾਪ ਉੱਤੇ ਹੀ ਰੁਕਦਾ ਹੈ, ਅਤੇ job ਦੌਰਾਨ ਜੋ ਇੰਸਟਾਲ ਕਰਦੇ ਹੋ ਉਹ runner ਨਾਲ ਗ਼ਾਇਬ ਹੋ ਜਾਂਦਾ ਹੈ। ਕੋਈ ਪੱਕਾ ਨੈੱਟਵਰਕ namespace ਨਹੀਂ ਜਿਸ ਨਾਲ client ਰਨਾਂ ਵਿਚਕਾਰ ਜੁੜਿਆ ਰਹੇ, ਇਸ ਲਈ ਹਰ ਰਨ ਉਸੇ ਅਪਹੁੰਚ ਅਵਸਥਾ ਤੋਂ ਸ਼ੁਰੂ ਹੁੰਦਾ ਹੈ। Azure ਇਹ ਸੀਮਾ Microsoft-ਹੋਸਟ ਕੀਤੇ agents ਲਈ ਦਰਜ ਕਰਦਾ ਹੈ, ਜਿੱਥੇ ਨਿੱਜੀ ਨੈੱਟਵਰਕਾਂ ਵਿੱਚ VPN ਅਤੇ ExpressRoute ਲਿੰਕ ਸਮਰਥਿਤ ਨਹੀਂ, ਅਤੇ GitHub-ਹੋਸਟ ਕੀਤੇ runners ਉਹੀ ਕਿਸਮ ਦੀ ਪਾਬੰਦੀ ਚੁੱਕਦੇ ਹਨ, ਵੱਖਰੀ ਕੋਈ ਵਿਧੀ ਨਹੀਂ। ਇਸ ਨੂੰ ਉਤਪਾਦ ਦਾ ਗੁਣ ਪੜ੍ਹੋ, ਬਾਈਪਾਸ ਕਰਨ ਵਾਲਾ ਦੋਸ਼ ਨਹੀਂ।
repository ਦੀ ਕੋਈ ਸੈਟਿੰਗ ਇਸ ਨੂੰ ਢਿੱਲਾ ਨਹੀਂ ਕਰਦੀ। job ਦਾ ਲੇਬਲ ਤੈਅ ਕਰਦਾ ਹੈ ਕਿਹੜਾ pool ਕੰਮ ਲਵੇਗਾ, ਅਤੇ ਹੋਸਟ ਕੀਤਾ ਲੇਬਲ ਭਾਵ ਪੂਰੇ ਰਨ ਲਈ ਤੁਹਾਡੇ ਘੇਰੇ ਤੋਂ ਬਾਹਰ ਦੀ ਮਸ਼ੀਨ।
ਨਿੱਜੀ ਪਹੁੰਚ ਇਸ ਲਈ ਤੁਹਾਡੀ ਮਲਕੀਅਤ ਦੀ ਕਿਸੇ ਚੀਜ਼ ਤੋਂ ਆਉਣੀ ਚਾਹੀਦੀ ਹੈ। runner ਉੱਥੇ ਰੱਖਿਆ ਜਿੱਥੇ ਨੈੱਟਵਰਕ ਪਹਿਲਾਂ ਹੀ ਖ਼ਤਮ ਹੁੰਦਾ ਹੈ, ਜਾਂ ਟਨਲ ਜੋ ਇੱਕ job ਜਿਊਂਦੀ ਹੈ। ਬਾਕੀ ਲੇਖ ਇਹਨਾਂ ਦੋਹਾਂ ਵਿੱਚੋਂ ਇੱਕ ਲਈ ਜਾਣ-ਬੁੱਝ ਕੇ ਭੁਗਤਾਨ ਕਰਨ ਬਾਰੇ ਹੈ, production ਵਿੱਚ ਬਿੱਲ ਲੱਭਣ ਦੀ ਥਾਂ।
ਰਨ ਦੀ ਬਾਰੰਬਾਰਤਾ ਨਾਲ ਫੈਸਲਾ ਕਰੋ, ਜਾਣ-ਪਛਾਣ ਨਾਲ ਨਹੀਂ
ਜਦੋਂ deploy ਦਿਨ ਵਿੱਚ ਕਈ ਵਾਰ ਆਉਂਦੇ ਹਨ, ਜਦੋਂ job ਇੱਕ ਤੋਂ ਵੱਧ ਨਿੱਜੀ ਪੋਰਟ ਨੂੰ ਛੂਹਦਾ ਹੈ, ਜਾਂ ਜਦੋਂ ਪਲੇਟਫਾਰਮ ਟੀਮ ਪਹਿਲਾਂ ਹੀ patching ਅਤੇ credential rotation ਸੰਭਾਲਦੀ ਹੈ, ਤਦ VPC ਜਾਂ ਦਫ਼ਤਰ ਦੇ ਨੈੱਟਵਰਕ ਵਿੱਚ ਆਪਣਾ runner ਵਰਤੋ। ਉਸ ਮਸ਼ੀਨ ਉੱਤੇ job ਕਿਸੇ ਵੀ ਹੋਰ host ਵਾਂਗ firewall ਪਿੱਛੇ resolve ਅਤੇ route ਕਰਦਾ ਹੈ। ਨੈੱਟਵਰਕ ਦਾ ਕੰਮ ਇੱਕ ਵਾਰ, ਇੰਸਟਾਲ ਵੇਲੇ ਹੁੰਦਾ ਹੈ, ਹਰ ਰਨ ਵਿੱਚ ਨਹੀਂ।
ਜਦੋਂ ਪਹੁੰਚ ਕਦੇ-ਕਦਾਈਂ ਹੋਵੇ ਅਤੇ ਤੁਸੀਂ ਤੁਹਾਡੇ ਨੈੱਟਵਰਕ ਵਿੱਚ credential ਰੱਖਣ ਵਾਲਾ ਲੰਮਾ ਜਿਊਂਦਾ agent ਨਾ ਚਾਹੋ, ਤਦ ਟਨਲ job ਦੇ ਅੰਦਰ ਬਣਾਓ। job ਕੁਝ ਮਿੰਟਾਂ ਲਈ ਆਪਣਾ ਨੈੱਟਵਰਕ client ਬਣ ਜਾਂਦਾ ਹੈ ਅਤੇ ਪਿੱਛੇ ਕੁਝ ਨਹੀਂ ਛੱਡਦਾ, ਜੋ ਅਕਸਰ ਪੂਰਾ ਮਕਸਦ ਹੁੰਦਾ ਹੈ।
ਟਨਲ ਚੁਣਨ ਤੋਂ ਪਹਿਲਾਂ ਇਸਦੀ ਲਾਗਤ ਦਾ ਅੰਦਾਜ਼ਾ ਲਓ। ਹਰ ਰਨ profile ਬਣਾਉਣ, client ਸ਼ੁਰੂ ਕਰਨ, ਤਿਆਰੀ ਅਤੇ ਢਾਹੁਣ ਦੀ ਕੀਮਤ ਦਿੰਦਾ ਹੈ। ਜੋ ਟਨਲ ਉੱਠਦੀ ਹੈ ਪਰ ਕਦੇ ਤਸਦੀਕ ਨਹੀਂ ਹੁੰਦੀ, ਉਹ ਅਜਿਹਾ deploy ਬਣਾਉਂਦੀ ਹੈ ਜੋ ਅੱਧ ਵਿੱਚ ਫੇਲ੍ਹ ਹੁੰਦਾ ਹੈ ਅਤੇ ਨਿੱਜੀ ਹਾਲਤ ਅਜਿਹੇ ਰੂਪ ਵਿੱਚ ਛੱਡਦਾ ਹੈ ਜਿਸਦੀ ਕਿਸੇ ਨੇ ਯੋਜਨਾ ਨਹੀਂ ਬਣਾਈ ਸੀ। ਉਸ ਤੋਂ ਬਾਅਦ ਦੀ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਘੱਟ ਹੀ ਸਾਫ਼ ਹੁੰਦੀ ਹੈ, ਅਤੇ ਦੂਜੀ ਅਸਫਲਤਾ ਆਮ ਤੌਰ ਉੱਤੇ client ਵਰਜ਼ਨ ਉੱਤੇ ਪਾਈ ਜਾਂਦੀ ਹੈ।
profile ਇਸ ਤਰ੍ਹਾਂ ਲਿਖਣਾ ਕਿ ਕਦਮ ਰੁਕ ਨਾ ਸਕੇ
profiles ਅਤੇ credentials repository ਤੋਂ ਬਾਹਰ ਰੱਖੋ ਅਤੇ ਉਹਨਾਂ ਨੂੰ secrets ਤੋਂ ਉਸੇ ਕਦਮ ਵਿੱਚ ਬਣਾਓ ਜਿਸ ਨੂੰ ਉਹਨਾਂ ਦੀ ਲੋੜ ਹੈ। ਕ੍ਰਮ ਤੈਅ ਕਰਦਾ ਹੈ ਕਿ job ਬਚੇਗਾ ਜਾਂ ਨਹੀਂ: sudo ਹੇਠਾਂ ਬਣੀ ਫਾਈਲ root ਦੀ ਹੁੰਦੀ ਹੈ, ਅਤੇ ਕਦਮ ਦਾ ਵਿਸ਼ੇਸ਼ ਅਧਿਕਾਰ ਰਹਿਤ shell ਫਿਰ ਉਸ ਵਿੱਚ ਨਹੀਂ ਲਿਖ ਸਕਦਾ।
- name: Bring up OpenVPN
run: |
umask 077
profile="$RUNNER_TEMP/client.ovpn"
printf '%s\n' "$VPN_PROFILE" > "$profile"
: > "$RUNNER_TEMP/openvpn.log"
printf '%s\n' "$VPN_CREDENTIALS" > "$RUNNER_TEMP/credentials.txt"
sudo openvpn --daemon openvpn-job \
--config "$profile" \
--auth-user-pass "$RUNNER_TEMP/credentials.txt" \
--writepid "$RUNNER_TEMP/openvpn.pid" \
--log "$RUNNER_TEMP/openvpn.log"ਉਸ ਬਲਾਕ ਦੀਆਂ ਦੋਵੇਂ ਵਿਸਤਾਰਾਂ ਅਗਲੇ ਕਦਮਾਂ ਲਈ ਹਨ। umask 077 ਨਾਲ profile 0600 ਮੋਡ ਵਿੱਚ runner ਵਰਤੋਂਕਾਰ ਦੀ ਮਲਕੀਅਤ ਵਿੱਚ ਆਉਂਦੀ ਹੈ, sudo ਅਜੇ ਵੀ ਪੜ੍ਹ ਸਕਦਾ ਹੈ, ਅਤੇ client ਆਪਣਾ log ਅਜਿਹੀ ਫਾਈਲ ਵਿੱਚ ਲਿਖਦਾ ਹੈ ਜਿਸਦਾ ਮਾਲਕ runner ਪਹਿਲਾਂ ਹੀ ਹੈ, ਇਸ ਲਈ ਤਿਆਰੀ ਦਾ ਲੂਪ ਦੂਜੇ sudo ਬਿਨਾਂ ਉਹ log ਪੜ੍ਹ ਸਕਦਾ ਹੈ। --writepid ਉਹ PID ਦਰਜ ਕਰਦਾ ਹੈ ਜਿਸ ਨੂੰ ਢਾਹੁਣਾ ਨਿਸ਼ਾਨਾ ਬਣਾਵੇਗਾ।
client ਸਰਟੀਫਿਕੇਟ ਨਾਲ ਪ੍ਰਮਾਣਿਤ ਹੋਣ ਵਾਲੀ profile ਨੂੰ ਆਪਣੀ ਕੁੰਜੀ ਉਸੇ ਤਰ੍ਹਾਂ ਲਿਖੀ ਚਾਹੀਦੀ ਹੈ, ਅਤੇ static-challenge profile ਨੂੰ ਮਿਲਦਾ auth ਮੋਡ ਚਾਹੀਦਾ ਹੈ। DNS ਵਿਕਲਪ push ਕਰਨ ਵਾਲੀਆਂ profiles ਨੂੰ Ubuntu ਅਤੇ Debian ਉੱਤੇ openvpn-systemd-resolved ਚਾਹੀਦਾ ਹੈ, ਕਿਉਂਕਿ ਇਕੱਲਾ push ਬੇਸ ਇਮੇਜ ਉੱਤੇ ਕੁਝ ਨਹੀਂ ਬਦਲਦਾ।
ਤਿਆਰੀ ਇੱਕ ਜਾਂਚ ਹੈ, ਵਿਰਾਮ ਨਹੀਂ
ਤੈਅ sleep ਉਦੋਂ ਤੱਕ ਹੀ ਟਿਕਦੀ ਹੈ ਜਦੋਂ ਤੱਕ concentrator ਹੌਲੀ ਨਾ ਹੋਵੇ, ਅਤੇ ਫਿਰ deploy ਕਦਮ ਰੂਟ ਬਣਨ ਤੋਂ ਪਹਿਲਾਂ ਹੀ ਚੱਲ ਪੈਂਦਾ ਹੈ। ਅਸਫਲਤਾ ਫਿਰ deploy ਟੂਲ ਦੇ ਅੰਦਰ ਨਿੱਜੀ host ਵਿਰੁੱਧ timeout ਵਜੋਂ ਦਿਸਦੀ ਹੈ, ਜੋ ਟਨਲ ਬਾਰੇ ਕੁਝ ਨਹੀਂ ਦੱਸਦੀ।
- name: Await private endpoint
run: |
for attempt in $(seq 1 30); do
if getent hosts private.example.internal > /dev/null && \
timeout 3 bash -c 'exec 3<>/dev/tcp/private.example.internal/8443' 2>/dev/null; then
echo "endpoint answered after $attempt attempts"
exit 0
fi
sleep 2
done
grep -Ei 'error|verify|auth' "$RUNNER_TEMP/openvpn.log" | tail -40
exit 1ਲੂਪ ਦੋ ਚੀਜ਼ਾਂ ਵੇਖਦਾ ਹੈ ਕਿਉਂਕਿ ਇਹਨਾਂ ਵਿੱਚੋਂ ਕੋਈ ਵੀ ਟੁੱਟਿਆ ਅੱਧਾ ਹੋ ਸਕਦਾ ਹੈ। getent hosts ਸਾਬਤ ਕਰਦਾ ਹੈ ਕਿ push ਕੀਤਾ resolver ਉਸ ਨਾਮ ਲਈ ਜਵਾਬ ਦਿੰਦਾ ਹੈ, ਅਤੇ socket ਖੁੱਲ੍ਹਣਾ ਸਾਬਤ ਕਰਦਾ ਹੈ ਕਿ deploy ਜੋ ਪੋਰਟ ਵਰਤੇਗਾ ਉੱਥੇ ਕੁਝ ਸੁਣ ਰਿਹਾ ਹੈ; ਸਿਰਫ਼ ਰੂਟ ਵਾਲੀ profile ਨੂੰ ਸ਼ਬਦੀ ਪਤਾ ਅਤੇ ਮਿਟਾਈ getent ਲਾਈਨ ਚਾਹੀਦੀ ਹੈ।
ਜਦੋਂ ਲੂਪ ਖ਼ਤਮ ਹੁੰਦਾ ਹੈ, log ਹੀ ਇੱਕੋ ਚੀਜ਼ ਹੈ ਜੋ ਤਿੰਨ ਵੱਖਰੀਆਂ ਖਰਾਬੀਆਂ ਨੂੰ ਵੱਖ ਕਰਦੀ ਹੈ: ਰੱਦ ਕੀਤਾ ਸਰਟੀਫਿਕੇਟ, ਗ਼ਲਤ auth ਮੋਡ ਅਤੇ ਅਜਿਹਾ ਰੂਟ ਜੋ ਕਦੇ ਨਹੀਂ ਆਇਆ। tail ਤੋਂ ਪਹਿਲਾਂ ਫਿਲਟਰ ਕਰਨਾ ਉਹ ਆਉਟਪੁੱਟ job ਦੀ ਅਸਫਲਤਾ ਦੌਰਾਨ ਪੜ੍ਹਨ ਯੋਗ ਛੋਟਾ ਰੱਖਦਾ ਹੈ। ਪਹਿਲੀ ਗ਼ਲਤੀ ਪੜ੍ਹੋ, ਆਖਰੀ ਲਾਈਨ ਨਹੀਂ, ਕਿਉਂਕਿ ਜੋ client ਪ੍ਰਮਾਣਿਤ ਨਹੀਂ ਹੋ ਸਕਦਾ ਉਹ ਲੂਪ ਹਾਰ ਨਾ ਮੰਨਣ ਤੱਕ ਆਪਣੇ ਆਪ ਨੂੰ ਦੁਹਰਾਉਂਦਾ ਹੈ। ip -4 route show dev tun0 ਇਸ ਸੌਖੇ ਸਵਾਲ ਦਾ ਜਵਾਬ ਦਿੰਦਾ ਹੈ ਕਿ ਕੀ ਰੂਟ push ਹੋਇਆ ਸੀ, ਅਤੇ ਉਸੇ ਲੂਪ ਵਿੱਚ ਇੱਕ ਲਾਈਨ ਦੀ ਕੀਮਤ ਹੈ।
ਢਾਹੁਣਾ ਅਤੇ artifact ਦਾ ਸਵਾਲ
job ਨੂੰ ਅਜਿਹੇ ਕਦਮ ਨਾਲ ਬੰਦ ਕਰੋ ਜੋ if: always() ਨਾਲ ਸੁਰੱਖਿਅਤ ਹੋਵੇ ਅਤੇ sudo kill "$(sudo cat "$RUNNER_TEMP/openvpn.pid")" ਤੋਂ ਇਲਾਵਾ ਕੁਝ ਨਾ ਚਲਾਏ। ਸਾਂਝੇ ਆਪਣੇ runner ਉੱਤੇ ਕਦੇ ਪ੍ਰਕਿਰਿਆ ਦੇ ਨਾਮ ਉੱਤੇ ਨਾ ਮਾਰੋ: ਕਿਸੇ ਹੋਰ job ਕੋਲ ਉਹੀ binary ਅਤੇ ਮਿਲਦੇ-ਜੁਲਦੇ ਆਰਗਿਊਮੈਂਟ ਵਾਲਾ client ਹੋ ਸਕਦਾ ਹੈ। ਫਿਰ ਅਸਫਲਤਾ ਦੂਜੇ ਨੰਬਰ ਉੱਤੇ ਚੱਲੇ job ਉੱਤੇ ਪੈਂਦੀ ਹੈ, ਅਤੇ ਉਸਦਾ log ਕੁਝ ਵੀ ਗ਼ਲਤ ਨਾ ਕੀਤੇ workflow ਵੱਲ ਇਸ਼ਾਰਾ ਕਰਦਾ ਹੈ।
ਸੁਰੱਖਿਆ ਇਸ ਲਈ ਮਹੱਤਵਪੂਰਨ ਹੈ ਕਿ ਅਸਫਲ deploy ਫਿਰ ਵੀ ਜਿਊਂਦੀ ਟਨਲ ਛੱਡਦਾ ਹੈ। always() ਬਿਨਾਂ ਸਫ਼ਾਈ ਦਾ ਕਦਮ ਠੀਕ ਉਦੋਂ ਛੱਡ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ ਜਦੋਂ ਇਸਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ, ਅਤੇ ਅਗਲਾ ਰਨ ਪਹਿਲਾਂ ਹੀ ਜੁੜੇ client ਸਾਹਮਣੇ ਸ਼ੁਰੂ ਹੁੰਦਾ ਹੈ।
ਕੋਈ ਵੀ ਡਿਜ਼ਾਈਨ ਅਪਣਾਉਣ ਤੋਂ ਪਹਿਲਾਂ ਇੱਕ ਸਵਾਲ ਬਾਕੀ ਹੈ: ਕੀ build ਕਦਮ ਨੂੰ ਸੱਚਮੁੱਚ ਨਿੱਜੀ ਰੂਟ ਚਾਹੀਦਾ ਹੈ? ਜੋ deployment agent ਪਹਿਲਾਂ ਹੀ ਨੈੱਟਵਰਕ ਦੇ ਅੰਦਰ ਬੈਠਾ ਹੈ ਉਹ ਮਨਜ਼ੂਰਸ਼ੁਦਾ artifact ਖਿੱਚ ਸਕਦਾ ਹੈ, ਜੋ ਆਮ builds ਨੂੰ ਨਿੱਜੀ ਰਾਹ ਤੋਂ ਦੂਰ ਰੱਖਦਾ ਹੈ। ਘੱਟ job ਜੋ ਟਨਲ ਰੱਖਦੇ ਹਨ ਭਾਵ ਘੱਟ ਥਾਵਾਂ ਜਿੱਥੇ ਪੁਰਾਣਾ ਰੂਟ ਲੁਕ ਸਕੇ।